| Commit message (Collapse) | Author | Age | Files | Lines |
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
`ExecStartPre` ran `alembic -c /opt/meshbay-hub/migrations/alembic.ini upgrade
head`. The build does stage that file, so the path existed and the contents
were wrong: `alembic.ini` resolves `script_location` with `%(here)s`, so the
copy pointed at `/opt/meshbay-hub/migrations/src/meshbay_hub/db/migrations` —
which nothing installs, because the migrations ship inside `meshbay_hub`, in
the shared venv.
`ExecStartPre` failing stops the unit. A hub installed from the RPM or the DEB
could not start at all, and nothing noticed because the one live deployment
was assembled by hand — the same shape as the node unit that carried `User=`
into the user unit directory.
The same `%(here)s` trap was already found once on the server, where a stray
`alembic.ini` resolved to a month-old snapshot of the tree. Twice is a trap
rather than an accident, so the fix is that the path is no longer written down
anywhere: `meshbay-hub migrate` asks the installed package where its own
migrations are, which is correct for the RPM, the DEB, a venv and a checkout.
The build stages no `alembic.ini`; the repo keeps its own for `alembic
revision` and for deploy scripts that already work.
`env.py` now prefers a URL the caller resolved over re-reading the environment
itself, so `migrate --config` connects with exactly the string the server
will — one resolution, not two that agree until they do not.
Six tests, three of which fail against the unit as it was. They read the
directives rather than the file, because searching the whole thing finds the
comment explaining a directive and calls that the directive.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4YmK41VsEURWFdop4EEeT
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
They were constants in two modules, so an operator could not touch them
without editing code and redeploying — and the hour a budget runs out is not
when anyone wants to do that.
`[mail]` in hub.toml carries the defaults; the live values live in
`hub_settings`, read at each use. A missing row falls back to what the
configuration file says, so an instance that never opens the panel behaves as
its file describes. The panel sends only what changed, the hub clamps each
value to a stated range and refuses a key it does not know, and the response
is what gets rendered — so a clamped value is never shown as stored.
`GET /v1/admin/mail` is the other half. There was no way to see any of this:
a refusal was a line in the journal, so an instance that had stopped sending
sign-up codes looked, from the panel, exactly like one with no sign-ups. It
reports the hour's use, what is left for sign-ups, and what is left for
recovery — the difference between those two being the reserved share made
visible.
Labels in all ten catalogues.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4YmK41VsEURWFdop4EEeT
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A second-machine sideload of the MSIX target surfaced three things the
earlier verification round (which only proved the package installs and
runs) had missed:
1. meshbay-node missing from PATH. installer.nsh's customInstall adds
node-runtime\ to HKCU\Environment at install time -- an unelevated
per-user write, never blocked by MSIX's no-elevation rule, only by the
more basic fact that an AppX/MSIX install runs no custom code at all.
packaging/win/ensure-node-path.ps1 (idempotent, no admin verb) plus
main.js's winEnsureNodeOnPath() do it from the app itself instead, once
per launch, shipped to Full and MSIX (not Light, nothing to add there).
Verified live via the Node inspector protocol: the entry was in
HKCU\Environment\Path after a launch, absent before.
2. A daemon that crashes on startup failed silently. spawnNodeDetached()
used stdio: 'ignore', so a real crash reproduced live (a second instance
colliding with the first on 127.0.0.1:18000) left waitForNode()'s
generic 60s timeout as the only failure ever shown. spawnNodeDetachedWatched()
pipes stdio and watches ~2.5s, rejecting immediately with the daemon's
own stderr on an early exit; a survivor has its streams released and
runs fully detached exactly as before. First version bounded the
captured text by line count and a live test showed that cut the actual
OSError line -- two uvicorn/asyncio tracebacks followed it in the real
capture -- so it is bounded by characters instead.
3. No hint that a startup-mode choice exists. The install-time radio page
was the only place this was ever offered, and nothing replaces it now
that no install-time page can exist at all. SetupWelcome (the existing
first-run banner) grew a conditional hint, shown only while a bundled
node is present and neither autostart nor service mode is configured
yet. Considered and rejected: linking straight to the Node page -- its
route is gated on a linked hub node key, false on the exact fresh-install
screen this hint targets, so the link would have been dead on arrival.
New key setup.node_startup_hint, added to all ten locale catalogues.
test_packaging_win.py gained six tests pinning all three (69 total).
Full plan and verification detail: C:\Users\admin\devel\msix-installer.md
section 13 (out of repo).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Store certification of the NSIS "MSI/EXE" submission failed on three
checks (silent-install verification, Add/Remove Programs entry, bundleware
check) -- traced and reproduced live to one cause: SmartScreen blocks an
unsigned, internet-downloaded installer at the shell layer before
Microsoft's own unattended validation bot ever gets to run it. MSIX
sidesteps this class of failure entirely: submitted through the Store's
native pipeline, there is no browser-download-then-launch step for
SmartScreen to intercept, and Microsoft signs the package itself at
publish time -- free, and specific to this submission type (Trusted
Signing remains a paid service for the MSI/EXE path). Full plan and
findings: C:\Users\admin\devel\msix-installer.md (out of repo).
electron-builder.msix.yml carries the same bundle as Full (node runtime,
ffmpeg, both service scripts) -- an AppX/MSIX install never elevates, by
design, but that changes only *when* the two elevated operations can run,
not whether the daemon ships. No main.js changes were needed: the on-demand
elevation path for service-mode (winElevateServiceMode(), driven from the
Node page) already existed for a different reason and depends only on
service-mode.ps1 being present as an extraResource, true for any packaged
Windows target. identityName/publisher/publisherDisplayName are the real
values from Partner Center's app-identity reservation, not placeholders.
build-win-msix.ps1 points electron-builder at the system Windows 10 SDK
(auto-detected) instead of letting it download its own bundled copy --
that download's 7z extraction creates symlinks this target never uses and
fails without SeCreateSymbolicLinkPrivilege, reproduced on this machine.
build/appx/ carries the four tile images the AppX target requires
regardless of showNameOnTiles, generated once from the existing app icon
(see that directory's README) since the system-SDK redirect has no vendor
samples to fall back to. build/appx-extensions.xml declares
windows.startupTask by hand rather than via electron-builder's
addAutoLaunchExtension, which always targets the Electron shell -- this
points at the bundled node binary instead, matching what "starts at sign
in" already means for Full.
Verified live via a signed sideload install (self-signed test cert,
cleaned up after): the package installs and the app runs correctly. One
finding worth carrying forward -- the declared network capabilities
(internetClientServer, privateNetworkClientServer) do not create any
firewall exemption for this app, most likely because automatic
capability-based exemption is an AppContainer-sandbox property and this
app deliberately runs full-trust, outside any sandbox. Not a regression:
no install-time elevation was possible either way, so the cost is the same
one-time OS firewall prompt firewall.ps1's own header already documents as
its fallback today.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
MeshBay Light ships the Electron client + UI only -- no PyInstaller node
freeze, no ffmpeg, no service install/autostart. Two standalone
electron-builder configs (Full via package.json's build field, Light via
electron-builder.light.yml passed with --config, which reads only that
file -- confirmed against app-builder-lib's own config loader) rather than
one config branching on a flag.
build-win-common.ps1 holds the steps both orchestrators share (Node check,
npm ci, Electron bump, sync-ui) so build-win.ps1 (Full) and the new
build-win-light.ps1 cannot drift apart; build-win.ps1 is refactored to
dot-source it with no behavior change (rebuilt and diffed byte-identical
output).
installer-light.nsh keeps the one thing Light still needs -- an
unconditional firewall rule, since the client listens too -- and none of
the service-mode/autostart machinery installer.nsh carries, which has
nothing to gate without a bundled node.
dist-light/ (Light's own electron-builder output dir) gets its own
.gitignore line since the bare dist/ rule does not match it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Two independent breaks in the Windows first-run path:
- node:start's win32 branch never linked the node's Ed25519 key to the hub
account, so the daemon sat at waiting_for_account and the Create Group
wizard span on "Detecting local node…" for ever — the only way through
was pasting the key by hand on the Profile page. The Linux branch has
always done this inline; factor it into linkNodeKeyAndAwaitRunning() and
call it from win32 too. PUT /v1/users/me/node_key overwrites, so this
also recovers an account still carrying a previous machine's node key.
- service.ps1's install branch did `$action = New-ScheduledTaskAction`,
shadowing its own [ValidateSet(...)][string]$Action parameter (PowerShell
variable names are case-insensitive). The CimInstance was coerced to the
string "MSFT_TaskExecAction", Register-ScheduledTask -Action rejected it,
and "background service" mode never created the task — reproduced live.
Rename the locals to $taskAction / $bootTrigger / $taskPrincipal.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
-LogonType S4U
schtasks.exe has no flag naming the logon type directly -- it only infers
S4U vs Interactive from whether /rp is present, and both readings broke
live on a blank-password account: /rp "" fails schtasks' own credential
validation, and omitting /rp registers "Interactive only", which never
launches the process at boot or on demand despite installing cleanly.
Register-ScheduledTask -LogonType S4U names the logon type explicitly, no
inference. Confirmed live: install, manual start, and unattended boot-time
start all now work.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Checked every instruction in packaging/README.md against the code. Four were
wrong; the rest hold.
- `meshbay-hub --generate-keys` does not exist. The hub CLI takes --config,
--log-level and a prune-groups subcommand, so the documented first step of
the hub install fails with "unrecognized arguments". It is also unnecessary:
app.py's lifespan generates the keypair on first start when the file is
absent. Step removed, behaviour documented instead.
- The systemd table named two source files that do not exist. The system unit
is built from systemd/meshbay-node.service and *installed as*
meshbay-node@.service; the user unit comes from meshbay-node-user.service.
Only the destination column was right.
- The output line pinned 0.9.0; every package is 0.10.0. Replaced with
<version> so it cannot go stale again, and MESHBAY_BUILD_DIR is mentioned.
- The firewall section documented only the client's casting profile. It now
covers the node profile added in 40abf09, with the LAN scoping that profile
requires.
Verified as accurate and left alone: the /tmp/meshbay-build/out/ path, the
install order, hub.toml.example, the desktop file, and the default.env claim
(true as of c2eade6, which implemented the copy it described).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtfG7z6wHWj8RKHCvxQtY1
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
winget install ffmpeg was considered and rejected as the mechanism: it
needs network access and winget/App Installer present at the exact
moment setup runs, and its failure mode is silent -- video just does
not stream, with nothing pointing back at ffmpeg. Not viable for a
non-technical install.
MeshBay transcodes browser-incompatible video to H.264 (-c:v libx264,
webrtc_server.py) -- a real encode, not remux -- so this needs a genuine
GPL ffmpeg build; no LGPL-only build includes an H.264 encoder, since
libx264 itself is GPL.
packaging/win/fetch-ffmpeg.ps1 (new)
Downloads, checksum-verifies and stages ffmpeg for the build. Source:
BtbN/FFmpeg-Builds' Windows x86_64 gpl-shared preset -- shared DLLs
rather than two independent static binaries, which is what nearly
tripled this: the "full" static build many devs already have via
winget is ~220 MB *per executable*. Pinned to one dated release tag
(immutable once published) and its own sha256, not the "latest" alias
BtbN repoints on every auto-build -- verified by hand first (downloaded,
hash matched, ran a real encode+probe with libx264) before pinning.
ffplay.exe (an SDL2 player, ~17 MB) is dropped; MeshBay never invokes
it. Cached after the first build. Runs its own smoke test (encode +
probe a real clip) so a broken fetch fails at build time, not for the
first user who tries to watch something.
packaging/win/LICENSE-ffmpeg.txt (new)
GPLv3 notice + where the corresponding source is, required because
this redistributes a GPL binary even though it is unmodified and only
ever invoked as a subprocess. Ships alongside ffmpeg.exe in the
installer.
build-node-runtime.ps1 / build-win.ps1
Bundling is now the DEFAULT, replacing the old opt-in -FfmpegDir (which
copied from a local directory and left most builds without ffmpeg at
all). -SkipFfmpeg opts out for a smaller, streaming-less local-iteration
build.
Also fixes a real bug the ffmpeg change exposed rather than caused: the
final `--help` smoke test did `$help -notmatch "meshbay-node"` against
$help captured as a PowerShell ARRAY (one element per line) -- -notmatch
on a collection is a FILTER, not a boolean test, and returns the
non-matching elements; any non-empty array is truthy in if() regardless
of content. Once --help wrapped past one line (it now does, with
autostart/service in the verb list) this threw unconditionally. Fixed
by joining to one string before matching, and pinned by a new test so
a future edit cannot silently reintroduce the collection-vs-scalar trap.
Verified: downloaded and hashed the pinned release by hand (matches),
ran a real libx264 encode + ffprobe against the extracted build,
fetch-ffmpeg.ps1 end to end (161 MB staged), a full build-node-runtime.ps1
run (308 MB node-runtime/) and a full installer build (MeshBay-Setup-
1.0.0.exe, 210.8 MB with ffmpeg bundled). Node suite 850 pass / 25 skip.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The per-user Startup-folder launcher (W3) only ever runs after this user
signs in. A real Windows Service would start earlier, but under
LocalSystem/NetworkService -- accounts with no normal profile, so
%LOCALAPPDATA%\meshbay\ (config, keystore, data) would not exist for it.
Relocating storage to make that work is real surgery, deliberately not
done here.
Instead: a Scheduled Task, created once with admin rights, that runs AS
THIS USER at boot without needing them to sign in first.
`schtasks /create ... /ru <user> /rp ""` with no `/it` registers an S4U
(Service For User) logon -- no password stored anywhere, and unlike
LocalSystem it loads this account's own profile, so config_dir()/
data_dir() need zero changes. The cost: S4U carries no network credential,
which the node never needed -- everything it touches is local disk plus
outbound internet. Creating the task needs admin (a boot trigger touches
system-wide scheduler state, the same reason /sc onlogon needed it);
querying/starting/stopping an existing one does not -- Task Scheduler
grants the owning user that much itself, which is what lets the Node
page's Start/Stop/Restart drive it with no further UAC prompts.
meshbay_node/platform.py
service_install/_remove/_status/_run/_end -- mirrors autostart_* but
for the Scheduled Task; TASK_NAME moved here (was decorative before)
meshbay_node/daemon.py
new `service install|remove|start|stop|status` verb; restart-daemon and
reset now check for the service task too
packaging/win/service.ps1
the installer-side equivalent (extraResource); status/run/end never
self-elevate -- only install/remove do, exactly matching what
Task Scheduler itself requires
packaging/win/service-mode.ps1
ONE elevated helper running service.ps1 + firewall.ps1 together, so
choosing service mode costs exactly one UAC prompt, not two
build/installer.nsh
the install-time choice: "run as a background service?" (one
elevation, both jobs) vs the existing per-user + separate firewall
question. Checked first, unelevated, so re-running setup with
everything already configured asks nothing. Uninstall offers the
matching one-elevation cleanup, default No.
src/main.js
winServiceTaskStatus/Run/End, wired into node:installed,
node:service-status/-stop/-restart and node:start: when the Scheduled
Task exists, drive it; otherwise fall back to the existing per-user
spawn/kill path. This is the hard requirement -- Start/Stop/Restart
from the Node page must work in either mode.
node-page.js / locales
a hint explaining why the per-user autostart toggle is absent when
service mode is active (info.mode from the backend, no new field to
gate on -- it just isn't sent in that case)
package.json: 0.1.0 -> 1.0.0.
Verified: electron-builder compiles the new NSIS choice logic and ships
all three scripts; service.ps1's S4U install fails cleanly (Access
denied) when run unelevated, and its status/run/end never touch "runas".
Cannot verify the elevated success path myself (no admin in this
session) -- that needs a real UAC click. Node suite 843 pass / 25 skip;
test_packaging_win.py pins the one-elevation property, the S4U flags,
and that main.js actually checks the service task in all three handlers.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Every run of customInstall showed the "Allow MeshBay through Windows
Firewall?" question and, on Yes, a fresh UAC prompt -- an upgrade or repair
install would ask again even with all four rules already in place.
customInstall now checks first: firewall.ps1 check, unelevated
(Get-NetFirewallRule needs no admin, only New/Remove do), exits 0 if every
rule already exists. Only a nonzero result reaches the MessageBox and the
elevated add. A second run of setup on an already-configured machine now
asks nothing.
Verified unelevated: check exits 1 and logs which rules are missing on a
machine with none of them (the fresh-install case); electron-builder
compiles the nsExec::Exec / Pop $0 / ${If} wiring. Node suite 837 pass /
25 skip.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
firewall.ps1 only handled WebRTC. The cast HTTP relay (src/cast-relay.js,
fixed TCP 19550-19553) and Chromecast/Smart TV mDNS discovery
(src/cast-chromecast.js, bonjour-service, UDP 5353) are a separate surface
on the client and were still hitting Windows' own "Allow access" dialog.
Now four named rules: MeshBay / MeshBay Node stay program-scoped with no
port restriction (there is no fixed WebRTC port to name -- the Windows
equivalent of the broad 1024-65535/udp range the Linux packaging needs,
since netfilter has no program scoping); MeshBay Cast / MeshBay Cast
Discovery add the program AND the fixed port, matching
packaging/firewall/*/meshbay-cast.xml exactly.
test_packaging_win.py cross-checks the port numbers against cast-relay.js's
own constants and the Linux firewalld definition, so the three descriptions
of one port range can't quietly drift apart.
Verified: rebuilt MeshBay-Setup-0.1.0.exe; the deployed firewall.ps1 carries
all four rules. Node suite 836 pass / 25 skip.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Installing used to mean clicking through two separate Windows "Allow
access" prompts later — one for MeshBay.exe, one for meshbay-node.exe —
each confusing on its own and worse before the exe carried a version
resource. Adding a firewall rule needs admin, and the installer is
deliberately per-user with no elevation, so this can only ever be opt-in.
packaging/win/firewall.ps1 (new, shipped as an extraResource at
resources\firewall.ps1): idempotent add/remove of the two inbound UDP
rules ("MeshBay", "MeshBay Node"), grouped, logged to
%TEMP%\meshbay-firewall.log. Locates both executables from its own path,
no arguments needed beyond the action.
build/installer.nsh: customInstall asks "Allow MeshBay through Windows
Firewall now?" and runs firewall.ps1 via NSIS ExecShellWait "runas" — one
UAC prompt — only when not ${Silent}; declining or dismissing UAC falls
back to Windows' own per-process prompts, unchanged. customUnInstall
offers the same in reverse, defaulted to No (a stale rule for a deleted
exe is inert, so this should not nag on the way out) and skipped for a
silent uninstall.
Verified: rebuilt MeshBay-Setup-0.1.0.exe (electron-builder compiles the
new LogicLib.nsh / ExecShellWait NSIS successfully); firewall.ps1 run
unelevated fails cleanly into its log ("Access is denied") rather than
silently doing nothing, confirming the fallback path. Node suite 835
pass / 25 skip.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
meshbay-node.spec now builds a VSVersionInfo (ProductName "MeshBay Node",
version read from the installed package so it tracks pyproject) and passes
it to EXE(version=...). The Windows Firewall prompt, Task Manager and the
file's Properties then show "MeshBay Node" instead of a bare exe name —
the prompt the operator has to answer on first run.
packaging/win/README.md gains a Networking section: the first-run firewall
prompt (allow Private AND Public — a VM adapter is Public), that a flat LAN
needs nothing else, what a routed/multi-subnet LAN additionally needs, the
libvirt-NAT caveat, and a pointer to the packaged Linux firewall profiles.
test_packaging_win.py covers the version resource.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
default.env was empty in every build, for three independent reasons:
1. build-node.sh read QE/node.env, which does not exist. Even pointed at the
real file it would have failed: its `grep MESHBAY_TMDB_DEFAULT_TOKEN=`
cannot match QE/tmdb.txt, which is a free-form note, not KEY=VALUE.
2. Nothing consumed default.env. packaging/README.md and build-node.sh both
claimed `meshbay-node init` copies it to <config>/node.env; grep found the
name in exactly two places, the README and the script that writes it. No
code implemented the copy, and `EnvironmentFile=-` hid the absence.
3. build-win.ps1 had no env handling at all, so Windows was empty for a
different reason than Linux.
Now: the build extracts the v4 read token -- tmdb.py sends `Authorization:
Bearer`, so it is the JWT, not the 32-char v3 key beside it in the same file --
matching KEY=VALUE first and then by shape, from MESHBAY_TMDB_TOKEN,
MESHBAY_TMDB_TOKEN_FILE, QE/node.env, QE/tmdb.txt. It writes default.env 0600
and *fails the build* if no token resolves; MESHBAY_ALLOW_NO_TMDB=1 opts out.
An empty default.env is invisible until a user opens Videos and finds no
metadata, which is how this shipped empty on two platforms at once.
platform.py gains packaged_default_env()/install_node_env()/load_node_env().
init copies the packaged file once, never overwriting an existing node.env,
and the daemon loads node.env itself at startup: systemd does this on Linux
via EnvironmentFile, but Windows autostart is a Startup-folder .vbs with no
equivalent. Already-set variables always win.
Also fixes an UnboundLocalError in main(): `config_dir` was assigned at the
top of the init branch, which made it function-local for all of main(), while
the reset branch calls `config_dir()` as the imported function. init returns
before that line, so `meshbay-node reset` could only ever raise. The local is
now cfg_dir.
Verified end to end on Linux: token baked (239 chars), init writes
<config>/node.env 0600 with it. The PowerShell half is written but unrun --
no pwsh on this machine.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtfG7z6wHWj8RKHCvxQtY1
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The packages carried a profile for LAN casting and none for the node's own
peer traffic, on the reasoning that the node exposes only a loopback admin
API. That is true of its administration surface and false of its transport.
WebRTC binds an ephemeral UDP port per connection, so there is no fixed port
to open, and a connection succeeds only if one side can initiate. Browsers
publish their host candidate as an mDNS `<uuid>.local` name, which aioice
cannot resolve on any platform and discards — so the node can never call a
browser back, and the browser must call the node. A node that refuses
unsolicited inbound UDP is unreachable from every browser on its own LAN,
leaving reflexive candidates, which fail whenever both peers share one public
IP and the router will not hairpin.
Hit twice in one session on two different hosts: a firewalld zone narrowed to
mdns + 19550-19553/tcp, and a ufw host with default deny-incoming. Both
presented as "the app cannot connect", neither as a firewall message.
Passive like the cast profile: packaged, not activated. The guide says to
scope it to a LAN zone or source, including the libvirt case, where traffic
from a guest to its own hypervisor is not masqueraded and so must be scoped
to the guest subnet rather than the LAN.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtfG7z6wHWj8RKHCvxQtY1
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The installer had no console entry point, so running the CLI meant cd-ing
into resources\node-runtime\ every time. installer.nsh now adds that dir to
HKCU\Environment\Path on install and removes it on uninstall, via stock
WordFunc.nsh (electron-builder's NSIS bundle has no EnVar plugin). It points
at the real meshbay-node.exe dir — not a shim — so `where meshbay-node`,
findNodeBinary and the W3 autostart launcher all resolve to the same binary.
A WM_SETTINGCHANGE broadcast nudges open shells; new ones just work.
Rebuilt: electron-builder --win nsis compiles the WordFunc includes fine,
MeshBay-Setup-0.1.0.exe (155 MB). test_packaging_win.py covers the add/remove
symmetry.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
`npm run dist:win` produces MeshBay-Setup-<version>.exe: the Electron client
and, beside it under resources/node-runtime/, the frozen meshbay-node daemon
(meshbay-common inside it). No hub. Per-user, no elevation — matches the W3
constraint that a logon-triggered scheduled task needs admin.
electron-builder / package.json
build.win nsis, build/icon.ico, extraResources -> node-runtime/
build.nsis oneClick:false perMachine:false allowElevation:false
allowToChangeInstallationDirectory:true
dist:win -> packaging/win/build-win.ps1 (mirrors dist -> build-client.sh)
packaging/win/
meshbay-node.spec + node-entry.py PyInstaller freeze of
meshbay_node.daemon:main. The awkward deps (aiortc, av, aioquic,
pydantic_core, uvicorn, watchdog, guessit, blake3, tzdata) are pulled
in whole with collect_all — that list is expected to grow when a frozen
run raises ModuleNotFoundError.
build-node-runtime.ps1 throwaway venv -> pip install -> PyInstaller ->
packages/meshbay-client/node-runtime/ (gitignored)
build-win.ps1 Node>=22 check, npm ci, Electron bump, sync-ui,
node runtime, electron-builder --win nsis
bump-electron.mjs the Chromium-CVE "build against latest Electron"
policy, out of the PS script (5.1 here-string terminator rules)
README.md
PyInstaller, not the python-embed zip: the frozen meshbay-node.exe is a
genuine relocatable single binary, which is what src/main.js:findNodeBinary
spawns (process.resourcesPath/node-runtime/meshbay-node.exe when packaged) and
what the W3 autostart launcher points at. The embeddable zip needs pip to make
that wrapper and the wrapper bakes in an absolute interpreter path.
build/installer.nsh: on uninstall, taskkill meshbay-node.exe and delete the W3
Startup .vbs (it would point wscript at a deleted binary every sign-in).
%LOCALAPPDATA%\meshbay\ — node.toml, keystore.enc — is never touched.
ffmpeg is not bundled by default (node finds it on PATH); build-win.ps1
-FfmpegDir copies ffmpeg.exe/ffprobe.exe in for a self-contained installer.
Verified on the Windows guest: PyInstaller freeze builds first try
(node-runtime 147 MB), frozen `meshbay-node status` talks to the live daemon's
loopback API; electron-builder --win nsis produces MeshBay-Setup-0.1.0.exe
(155 MB), oneClick/perMachine flags applied, node-runtime bundled at the path
findNodeBinary expects. test_packaging_win.py (14) pins the config invariants
and the NSIS <-> platform.py autostart seam. Node suite 798 pass / 34 skip.
Open: Authenticode signing (13.9 — unsigned => SmartScreen), Windows CI
(18.3), electron-updater. First clean-machine install + DPAPI + autostart
round-trip is a manual check.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
package twice
Chromium CVEs are fixed in Electron releases, and a client built against
an old one ships those holes to every user. That is a certain harm; a
build that breaks on a new Electron is a repairable one. build-client.sh
now bumps to the latest on every build, prints the comparison, and lets
the build fail if it cannot cope — the failure is the signal to fix, not
a reason to stay behind. It writes package.json and the lockfile on
purpose: the new pin is meant to be committed. A registry it cannot reach
is a warning, not a failure.
Exercised by pinning back to 42.9.2 and building: "==> Electron 42.9.2 ->
44.1.1", exit 0, electron=44.1.1 in the packaged output. Note npm audit
would have said nothing about any of this — Chromium CVEs fixed in
Electron do not reliably reach the npm advisory database.
Separately, package.json declared linux.target [deb, rpm] with its own
deb/rpm depends, so `npm run dist` built a second package under the same
name. The two had drifted: /opt/MeshBay/meshbay-client against
/opt/meshbay-client/meshbay, and Depends: python3-meshbay-common naming
none of the Electron runtime libraries the real DEBIAN/control lists — it
would have installed cleanly and then refused to start. Nothing in the
tree referenced `npm run dist`, which is why the drift was free to
happen. That config is gone, "dist" delegates to build-client.sh, and
test_desktop_shell.py refuses its return.
`--dir` was re-run with no linux block at all: exit 0, same binary
build-client.sh consumes. It needs appId, productName and files, nothing
else.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AbwJDbNTkiRUh7HTWEoyss
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Registering from the native client failed with `captcha_failed` while the
checkbox was green — a worse symptom than the one being fixed, because the
widget now looked fine and only the hub's own log said otherwise:
captcha solved on an unexpected host ''; allowed: ['localhost', 'meshbay', 'meshbay.org']
The previous commit assumed Google would report the host component of the
origin, so `app://meshbay` would come back as `meshbay` and could sit in
`allowed_hosts`. It does not. A solve Google cannot attribute to a domain
reports an **empty** hostname, and no allowlist entry can match that. An empty
entry is not the answer either: a blank in a TOML list is a typo far more
often than an intention, and `load_config` drops blanks for that reason —
`captcha.allow_unattributed_host` is a named flag instead, so the trade is
stated where it is made.
What it admits, plainly: every non-web client, not only ours. A file:// page
or somebody else's Electron application look identical from here. That is the
same bar the client's own origin would have been — main.js already records
that `app://meshbay` is not a credential — and it is a bar: the captcha still
has to be solved, per token, in something that can render it. What is given up
is the origin restriction for non-web clients, not the captcha. Off by
default, and a hub without the desktop client should leave it off.
The refusal now names which of the two it is, since they need different
answers: an unexpected host names the host, an unattributed one says to set
the flag.
docs/captcha.md §6 said `meshbay` was the value and told operators to add it;
it now records what was measured and why the guess was wrong. The packaged
example config carries the flag with the same warning.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014UtzVrzM7e2tG9fSpkR9ML
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
`build-client.sh` runs `npm ci`, which deletes node_modules wholesale, then
re-extracts Electron's dist. A chrome-sandbox that had been made root-owned
4755 for local development comes back 755 and owned by whoever ran the build.
Running the app straight from node_modules then aborts outright:
FATAL: The SUID sandbox helper binary was found, but is not configured
correctly. Rather than run without sandboxing I'm aborting now.
Chromium refusing to start beats it quietly dropping the sandbox, and that
refusal is baffling if the crash is not connected to a package build run
minutes earlier — the two look unrelated, and the file's own mtime is 1980
either way, so nothing on it points at what happened.
The build now says so, with the command to put it back. Said and not done: a
build script has no business setting a setuid bit behind someone's back, and
this one uses no sudo. The packaged app was never affected —
packaging/deb/meshbay-client/DEBIAN/postinst does the chown+chmod at install
time, which is where it belongs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014UtzVrzM7e2tG9fSpkR9ML
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Three defects, found while answering whether installing the .deb would land
where the production server was just moved to by hand.
- **The example config was never packaged.** `build-hub.sh` copied
`packaging/conf/hub.toml.example` under `if [ -f ]`, and that path does not
exist in this repo — so every package ever built shipped no example at all
and said nothing about it. The postinst places no config either, on purpose
(a shipped hub.toml is overwritten on upgrade; a shipped secret gets run in
production), which left an installed hub with nothing to copy from. The file
now exists, documents every key `config.py` reads including the captcha
`allowed_hosts` the desktop client needs, and the copy is a hard failure
rather than a silent skip.
- **`/etc/meshbay` was created 0755.** It holds the hub's Ed25519 private key
and its database password. The file modes protect the contents, but a
world-listable config directory tells anyone with a shell what a hub keeps
and where. Now 0750 root:meshbay, in both the deb postinst and the rpm
scriptlet; the service reads it by group.
- **The rpm would have failed to build on the new file.** `%files` claimed
nothing under /etc, and rpmbuild refuses an installed file no line claims.
It now declares the directory and the example, with explicit `%attr` and
`%config` so an operator's edits become .rpmsave rather than vanishing.
Package modes no longer follow the builder's umask either — the same source
tree produced 775/664 on a machine with umask 002 and 755/644 with 022.
`install -m` sets them.
Verified by building: the deb now carries ./etc/meshbay/ at drwxr-x--- with
hub.toml.example at 0644, and the embedded postinst tightens the directory as
belt and braces rather than as the only thing making it right. The rpm path is
unverified — no rpmbuild on this machine.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014UtzVrzM7e2tG9fSpkR9ML
|
| |
|
|
|
|
|
|
|
|
| |
The directory held a single DEBIAN/control still pinned to 0.2.0. Nothing
builds it: build-packages.sh packages meshbay-common (the bundled-venv
deb under packaging/deb/meshbay-common/), which replaced the old
system-Python python3-meshbay-common package.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QNPfgH6VWcRzJDZGuzy1jJ
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Remove the node daemon's server-rendered admin UI (GET / and /audit, the
_render_* helpers and inline templates) and the `meshbay-node ui` CLI verb.
The loopback control API stays; it is now JSON only, ruff-clean, and 453
lines (was 1074). Also drop three never-wired endpoints (/api/config,
/api/chat/history, /ws/chat, plus broadcast_chat) and the pointless
18000/tcp firewall profiles.
The desktop client's Node page (static/node-page.js) takes over what the
dashboard showed, reorganised into six tabs (Overview, Groups, Roster,
Peers, Audit, Settings):
- Overview: version, node id, QUIC port, hub, index-cache maintenance
- Roster: node-wide view with unpin
- Peers and Audit: auto-load on open, no Load button
- Audit: real usernames and group names (resolved from the roster and
node.toml), Previous/Next pagination newest-first, Export CSV of every
matching row
- Settings: node settings, STUN, ICE, denylist, then Unlink from hub
Backend: audit.get_entries gains `offset`; /api/audit and /api/peers
resolve ids to names via a new _display_names helper; CSP tightened to
default-src 'none' now that no HTML is served. draft-v6 sections 2.11 and
2.12 corrected -- the Node page uses the loopback API, not MNP.
One capability is intentionally dropped: browser-based admin on a headless
server. The CLI covers every operation there.
See docs/refactor-node-ui.md.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MQCaZnde4Bjjdu84dhSuF5
|
| |
|
|
|
|
| |
Packaging system complete and verified on Ubuntu 26.04 and Fedora 44.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Shared venv architecture: meshbay-common owns the Python venv with all
pip deps pre-installed; hub and node add only their code into it.
Client is a standalone Electron app. No pip runs at install time.
- Add build scripts (packaging/build/) for common, hub, node, client
- Add orchestrator build-packages.sh with deb/rpm auto-detection
- Add .deb control/postinst for all 4 packages
- Add .rpm specs for all 4 packages (replaces python3-meshbay-common)
- Add Gnome .desktop launcher and icon resizing
- Add firewalld services (meshbay-cast, meshbay-node) and UFW profiles
- Update systemd units to use /opt/meshbay-common/venv/bin/ paths
- TMDB token baked into node package at build time via QE/node.env
- Fix package-lock.json sync for protobufjs override
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
nothing
search_release() only ever tried a field-scoped exact-phrase Lucene query
(artist:"..." AND release:"..."). Verified live against musicbrainz.org:
any deviation from MusicBrainz's own spelling (a year suffix, an edition
tag, an artist credited under an older/aliased name) drops it to zero
results outright rather than a low-scored one, so the confidence check
never even ran — this is why well-recognized artists were still getting
almost no cover art. Add an unscoped loose-query fallback, escape Lucene
special characters in the interpolated tag text, and make the confidence
score consider the artist match too (not just the album title) now that
the fallback has no field scoping to rely on.
Also document MESHBAY_MUSICBRAINZ_CONTACT_DEFAULT in the systemd units
and man page, mirroring MESHBAY_TMDB_DEFAULT_TOKEN's precedent — never a
literal value in source, configured via node.env.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013XSohfUQQiaE77qyFLgSv3
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Implements docs/mediacenter.md: a "Videos" group application built on the
existing files index rather than a separate catalogue. On the node side,
new indexer enrichment (technical probe, filename/season parsing, thumbnail
generation) runs per-file once an operator has chosen a video_root for the
group, plus a TMDB client for on-demand poster/metadata lookups (never
client-side, thumbnails delivered over the existing chunk path). On the hub
side, a new video-app.js renders a lazily-mounted poster grid or a
thumbnail-only flat list, with TMDB entirely optional per group.
Along the way: the global apps registry now drives Settings' default-tab
picker instead of a hardcoded list, and the video_root is configured from
group Settings (like uploads) rather than from Files, with the node
refusing to run any TMDB/thumbnail work until one is set.
Fixes several bugs found via live testing against a real library, notably
a race between two effects writing the same "image ready" state that could
leave a poster grid spinning forever on a same-tab revisit — see
mediacenter.md §5.4 for the full account of each one.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
The group UI's applications split, and the two missing-import bugs it
surfaced and fixed along the way.
MNP goes to 0.4: apps_enabled/apps_enabled_ack, and enabled_apps on the
handshake ack, for the group-applications registry. Additive — a node that
predates it is never sent the op, and a client that predates it never looks
for the field.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016SF6RKNBKg9qejmoMJ9ybA
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
Node daemon no longer blocks startup on slow directory scans — initial
indexing runs in the background so the node reaches "running" immediately
after transports are up. Fixes the wizard failing to detect the node when
large USB/NAS roots take minutes to scan.
Also: wizard key-linking deadlock resolved (main.js links during poll),
invite form stays in DOM during reconnects (disabled instead of destroyed),
pairing code bridges to renderer, and firewall docs for LAN casting added.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Stage D, and the honest half of it.
D1 — the seam (done, and verified)
----------------------------------
`static/platform.js`. `HUB` becomes `platform.hubBase()` and the transport is
built with the same base, so one address has one source. In a browser it returns
'' and every path stays relative to the origin that served the page — the
acceptance criterion for this split was "the browser SPA behaves identically",
and it does. `platform.js` joins `_ASSETS`, or a change to it would not move the
content hash and a cached browser would never ask for it.
D2 — the shell (written, never launched)
-----------------------------------------
**There is no npm on this machine. Electron was never installed and
`packages/meshbay-client/` has not been run once.** That is stated here rather
than discovered later.
What is there: a main process serving the packaged interface over a privileged
`app://` scheme (`secure` and `standard` are not cosmetic — without them the
service worker refuses to register and streamed downloads break silently), a
preload exposing an enumerated bridge that never passes a filesystem path, a
window with `sandbox`, `contextIsolation` and no node integration, navigation
away from the package refused, and a CSP where the hub is reachable over
connect-src and is not a script source. The hub address arrives as a process
argument because `platform.hubBase()` runs before anything can await.
`test_desktop_shell.py` pins each of those by reading the source — the treatment
`test_downloads.py` already gives the three browser save paths. It catches a
property being removed and proves nothing about the application running. Two
were checked by breaking them.
The interface is *copied* into the package by `build/sync-ui.js` from the hub's
static directory, and `ui/` is gitignored: a silent fork is the only real way to
end up maintaining the interface twice.
D3 — partial
------------
The bridge, and the part worth having now: safeStorage's backend is reported
rather than assumed. On Linux it falls back to a fixed key when no keyring is
running, silently — someone who believes the OS is holding their keys is told
when it is not. The native key lifecycle belongs with D4 and needs a running
application to mean anything.
D8 — partial, and a real defect found
--------------------------------------
`meshbay-node.spec` installed the SYSTEM template — the one carrying `User=%i` —
into `%{_userunitdir}`. A user unit already runs as its owner and cannot carry
`User=`; systemd refuses the file, so the packaged unit could never have
started. Nothing noticed because nobody had built and installed the RPM.
Two units now: the template to `%{_unitdir}`, and a new `meshbay-node-user.service`
that a person enables themselves without a password — which is what lets the
desktop client install a node without asking for one. It carries ExecReload, so
`meshbay-node reload` does not have to stop a service somebody is streaming from,
and documents the drop-in for a drive outside the home, RequiresMountsFor
included.
798 tests pass; e2e.py still passes end to end. Nothing here was built or
launched: no npm, no rpmbuild.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A design discussion on 2026-08-17 settled Phase 13 and, in doing so, changed
four things the spec states. v6 restates only those; v5 stays authoritative for
everything it does not touch, per the convention v5 itself used with v4.
What changed:
* The native shell is **Electron**, not pywebview — structural decision 18
reversed. The SPA depends on Chromium-class APIs (WebRTC, WebCrypto
X25519/Ed25519, MSE, Service Workers), so keeping Chromium keeps
transport.js, crypto.js, keyderive.js, downloads.js and sw.js *as the
client*. A system webview meant reimplementing ~2500-3000 lines. The old
"69 % reused" figure was measured against an app.js of ~2600 lines; it is
4586.
* A group's content is **several named roots**, not one directory, because
the planned video and audio libraries will not live in one folder on one
disk.
* **Device linking**: one person may hold several devices on a node,
admitted by a key the node already pinned and bound by a one-time code the
new device generates. Without it a native client is refused where a browser
is not, and an account created natively could never be opened in a browser.
* **Authorship is authenticated, not asserted** — chat senders sign, uploads
have a provable owner, and delete authorization moves from the uploading
key to the account.
And one rule v5 assumed without writing down: **group-related server state
lives on the node.** Verified for multi-root — SwarmSource carries hashes and
endpoints, no paths.
Also here: the Caddy configuration, which was a snippet in the roadmap that
would have broken the SPA (it predates /a/<hash>/ asset versioning and would
have 404ed /sw.js, silently killing streamed downloads on Firefox and Safari);
and downloads.html, which becomes a security page once a release key exists.
Phase 15 was re-read against device linking and is wrong as written: a sender
key must be per **device**, never per person, or two devices sharing a chain
produce key and nonce reuse — C1 again, one level down. senderkeys.py already
fails this silently.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Two rounds of features and one long hunt.
The hub gained leaving a group, a cap of ten live public groups per owner, and
the rule that a group is listed only once a node has announced it — with
`prune-groups` to collect the ones that never got one. Presence rides on the
group list, from the registry the hub already keeps for signaling. The web
client speaks ten languages, splits Profile from Settings, and reads chat the
way it is written: newest first, paging backwards.
The rest was one symptom — "close the viewer, the next video hangs" — with three
independent causes underneath, none of which the test suite or e2e.py could see.
A background task the loop only weakly referenced, collected while it held a
transcode slot. A connection-state handler that forgot a peer without stopping
it. And `await proc.wait()` deadlocking on ffmpeg's own unread output, which no
amount of SIGKILL resolves. Found by instrumenting the node and reading the log,
after two confident fixes that addressed real but different bugs.
MNP goes to 0.2: PING/PONG and backward chat paging, both additive.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The web client speaks ten languages instead of one: French, Spanish,
Brazilian Portuguese, Simplified Chinese, Japanese, German, Italian, Dutch
and Polish, all formal, with `hub`, `node` and `GEK` deliberately left in
English so the interface still matches the CLI and the docs. Catalogues
are fetched per language rather than shipped together, plural forms go
through Intl.PluralRules because Polish needs four of them, and locale
matching keeps the region so pt-BR and zh-CN resolve to the files written
for them.
Splitting one module into a loader and ten catalogues gave the SPA a
version dependency it did not have before, and the hub was serving static
assets with no explicit freshness at all. A browser that cached half a
deploy either rendered every string as its own key or, in the other
direction, failed to link the module graph and showed nothing. Static
responses now carry no-cache, which costs one conditional request and
answers 304 with no body.
The node and common packages carry no functional change; they move with
the version because the three are released together.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Twenty-one commits since 0.2, and enough of them change what the thing
does that moving the old tag would have been the wrong description.
Node: video streaming paced by the client rather than pushed at it, and a
stream that ends when the viewer closes instead of holding a transcode
slot for two minutes. An operator can remove an empty directory and
revoke a member over MNP. `meshbay-node group add` attaches another hub
group without hand-editing node.toml. The node.toml operator key is gone;
the roster is the only source of authority.
Hub and web client: transfers outlive the page that started them, with a
widget that shows the rate and can cancel them; downloads stream to disk
in every browser, through the File System Access API where it exists and
a service worker where it does not; a folder can be taken as a zip built
in the browser. A group owner can remove a member and edit the
description. Nodes are recorded at the address their signed announcement
arrived from, not the one STUN told them about. Deleted accounts stop
being counted while the connection log keeps their name.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
| |
All three packages together, as the conventions require, plus the RPM and DEB
metadata and their changelogs.
The tag said 0.2 while every package announced 0.1.0, which would have shipped an
RPM claiming to be the reviewed build while containing a different protocol: the
hub schema lost the user identity keys, tokens lost pk_user, and
gek_bundle_store left the wire. Pre-1.0, a breaking change bumps MINOR.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The node daemon was previously a skeleton that only started QUIC/TCP
servers and the local web UI. All browser-facing functionality (WebRTC,
hub WebSocket, chat store, HTTP file API) lived in QE demo scripts.
This rewrites daemon.py to be fully self-contained:
- WebRTC transport for browser clients (aiortc DataChannel)
- Hub WebSocket task (signaling, revocations, WebRTC offers)
- ChatStore per group (SQLite in ~/.local/share/meshbay/)
- HTTP file API per group (create_http_app on configured port)
- Graceful shutdown (all transports, stores, tasks)
- hub_client: _ws tracking + send_ws() for chat notifications
- config: data_dir field for persistent state
- systemd: security hardening (ProtectSystem, StateDirectory)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
|
3 packages: python3-meshbay-common (dep), meshbay-hub, meshbay-node.
RPM: spec files with pre/post scriptlets (useradd, systemd macros).
DEB: DEBIAN/control + postinst for hub, control for node + common.
Systemd: hub.service (system, security hardening) + node.service
(user template @%i, EnvironmentFile for MESHBAY_UNLOCK_KEY).
packaging/README.md: build + install instructions.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
|