| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Found by the first Windows beta tester, then reproduced on a clean install.
After a service-mode install nothing set the node up for the account that
signed in: the boot task started a node that quit ("hub.username not set"),
and the sidebar showed Node / Create group only once the hub held a node key.
The only way to the wizard that provisions was the home page's welcome card,
which an account already in a group never sees. The way out was
`meshbay-node init` and the key pasted on the profile page -- which is also
what PACKAGING-GUIDE.md told people to do.
- main.js `node:ensure`, called by app.js at sign-in: provisions, starts and
links the node this build ships (Windows, bundled node only). A node set up
for another account, or an account linked to another node, is left alone.
node:start waits for it, so the two never race.
- The sidebar shows the Node section when a node exists on this machine.
- The Node page's status is the node's: its control API and the process
list, not the service task's state (a node started from a terminal ran
while the page said Stopped). Stop says Stopped only once no
meshbay-node.exe is left, and stays offered for a process that answers
nothing.
- CLI stop kills the pid that answered when a graceful stop does not finish,
and fails with the reason when a node process is still there.
- The daemon ends its process 3s after _shutdown(): Python's exit waited for a
busy indexer thread, with the control API already closed. Armed by main()
only, never by a daemon run inside a test.
- node.toml is read as utf-8-sig (PowerShell 5.1 writes a BOM), and a config
that cannot be read is logged instead of dying silently in service mode.
- "Pair this browser" queues the code for the next group of this node to
open instead of saying "Paired successfully"; no banner before a group.
- test_e2e_windows_app.py (opt-in, MESHBAY_WIN_E2E=1) drives the installed
app against a throwaway hub: fresh account to linked node, Stop, Start,
Restart, checked against the real processes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
| |
On Windows watchdog cannot tell what a deleted path was -- it is gone -- and
reports a directory moved out of the root as a file deleted, so the fix in
3d5a168 never ran there and test_the_watcher_reports_a_directory_moved_out
failed. A deleted path the index still holds entries below was a directory.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
| |
On Windows a bare `read_text()` or `subprocess.run(text=True)` decodes with
the locale's code page (cp1252), not UTF-8. `test_licensing.py` then failed on
a byte of the vendored LICENSES.txt, and `test_keyring_vectors.py` decoded the
generator's output, which carries CJK test strings, into something that no
longer matched keyring.json. Both files are UTF-8; say so.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
| |
A member who joined by link, by a new device or into an open group was
pinned in the roster with no name, so the audit log showed only the
first characters of their id. The hub's MNP token now carries the
account's username, and after the handshake the node writes it into the
roster for an account whose name is empty. An invitation's name is never
overwritten; the name is a label, authority stays on `sub`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
Every full index walked all roots on the loop, and a node with several
large roots stopped answering for seconds. Walk directories only, on the
roots' disk thread; index_sync is spawned and still answers on failure.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
A node busy indexing could miss the single 3 s check after a root was
added, and the button stayed hidden until a reload. Ask up to four times
before deciding there is no node on this machine.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
| |
The Yes/No before it defaulted to No, so they stayed behind. Read both
unelevated and raise the UAC prompt only when one is left.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
Watchdog reports such a move as one "directory deleted" event and nothing
for the files, which the indexer ignored until the next reconcile. The
freeze rules still apply: root live, directory gone, parent present.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
Chunks sent while the reconnect's connect() runs throw at once, and six
retries 1.5 s apart ran out before the reconnect landed. Wait for it, up
to two minutes, without spending retries. Follow-ups parked in §15.3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
9269374 let the transfers header wrap, which broke the one-line header
test_layout_measured enforces. The button now sits beside what it clears,
and the header is title and summary only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
| |
The header (title, summary, clear button) overflowed the 330 px panel in
French. Let it wrap, with a gap, and hide horizontal overflow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
shutil.which never looks beside meshbay-node.exe, and node-runtime is
appended to the user PATH, so any earlier ffmpeg.exe on PATH ran instead
of the pinned copy. Log the resolved media tools at startup.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
| |
An iPhone has no MediaSource; every film was refused as an
unsupported codec. A browser with neither now says so.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
| |
The line sat above the results for as long as the page was open. It is now
said once a cross-group pass is over, for five seconds, in the same passing
note as "Link copied" — moved out of copy-link.js into note.js (`say(text,
ms)`), one note at a time for the whole page. The `.search-unreachable` rule
goes with the line it styled.
The copy-link probe now also checks in Chrome that the note goes by itself.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
| |
The invitation probe listed no groups, so after Join the address stayed the
#/group/<id> the button navigated to and the group links' rewrite to
#/name@owner was never exercised on that path. A third case has the hub list
the group once joined, as it does: the group page opens, the address shows
the handle without a history entry of its own, and the code still never
reaches the hub.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
and Search
"Copy link" puts the address group-link.js resolves on the clipboard, on the
hub's origin rather than the page's, so a link copied in the desktop
application is not app://meshbay. Files offers it for one row, from the
right-click menu or the toolbar with one row ticked (a phone's way in);
Music on one track's menu, whose dots a phone has; Photos on a right-clicked
tile and in the lightbox's bar. The video player and the file preview carry
a link button next to Download.
Applications get a `linkFor(entry | folderPath)` prop (MESHBAY_DESIGN.md
§9.2) and offer the action only when it names a link. The group page builds
it from the hub's row; Search from each result's own group and its path
before the merged views prefixed it, and names no link for a folder of the
merged tree, which a group name alone does not identify.
harness/copy_link_probe.py mounts the three applications in Chrome and reads
what reached the clipboard.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A group can now be reached by the handle shown under its name, and a path
after it points inside the group: #/name@owner/root/dir/file downloads the
file and opens Files on its folder; a folder opens Files there. The handle
is resolved in the client against the account's own /v1/groups/mine, so no
hub route answers for a name and nobody can probe for one. While a group is
open the address shows the handle (replace, no history entry); a linked path
is taken out of the address once acted on, so a reload does not download
twice.
Signing in no longer sends everyone home: the form stood in for the page the
address named, and that is where a link opened signed out was going.
group-link.js holds the parsing and lookups, executed whole by
test_group_link.py; harness/group_link_probe.py drives the router in Chrome.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The Members list showed the hub's membership, which an account gains when it
accepts the invitation or redeems a link, before it has presented its code to
the node. The node's roster is the authority (MESHBAY_DESIGN.md §3.4), so
the list now crosses the hub's members with the sealed group roster the node
already sends every connected member. An account the node has not admitted
yet is shown to the owner alone, as waiting for its code, with the Remove
button; other members do not see it. When the roster cannot be read, the
hub's list is shown as before.
groupRoster() takes { fresh: true } so the page sees who joined since the
connection opened.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A node started with the desktop session runs before the session has mounted
its USB drives. The safety net then auto-ejected every removable root and
persisted it exactly like an operator's eject, so after each reboot those
roots stayed ejected until someone plugged them by hand (seen on a node whose
/media drives were mounted a minute after it started).
An auto-eject is now stored as such ("auto" in roster.db). At startup and at
every reconcile, an auto-ejected root whose path is readable again is checked
against a few files the hash cache knows under it, at the same path with the
same size and mtime; one found and the root is plugged back and rescanned.
An empty mount point or another drive in its place is not recognised and
stays ejected. An operator's eject is never undone automatically.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
| |
The hub address is where the person's account lives, so it sits with the
account: on the Profile page, after Sessions and before deleting the account.
Settings keeps "Keys on this device", which describes the machine.
The hint now says what the setting is: the hub this application connects
to, and that changing it signs you out while the account stays on that hub.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
docs/MESHBAY_HTTP_API.md lists every route of the hub (by domain, with the
authentication each requires) and of the node's loopback control API. It is
written by docs/generate_http_api.py from the routes and their docstrings;
test_http_api_doc.py fails when the file drifts from the code or when a
route has no docstring, so a new route must say what it does.
79 routes had no docstring and get a one-line description; a few whose first
line did not describe the route get a summary line.
The login page's developer docs gain an API link next to Design and
Protocol, in every language. README, MESHBAY_DESIGN.md (§0.1, §6.7, §7) and
CLAUDE.md point to the listing; README also points to examples/.
The examples scripts with a shebang become executable.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
The first-run "Which hub?" field now starts filled with meshbay.org; it is
still asked, so the client can be pointed at another hub. The note under the
form, which explained there was no default, is removed with its catalogue key.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
The daemon line repeated the state the daemon reports ("running — running").
The state is now appended only when it says something more than "running",
such as waiting_for_hub. The QUICKSTART example is updated to match.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
node:op only knew the daemon's token once a page had called detect(), and
kept it after the daemon replaced it on restart: the index dock stayed empty
on a node machine until the Node page was opened, every operation answered
401 after a node restart, and on a machine without a node each 30 s poll was
a rejected IPC call Electron printed to the terminal ("Node not detected").
node:op now reads the config and token from disk on every call, and the dock
asks detect() before its first operation and after any failure.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The protocol layer is LGPL-3.0-or-later in every language it exists in, so
any client may use it whatever its own licence: meshbay-common, and the files
marked with an SPDX line — keyderive.js, crypto.js, playlist-crypto.js,
transport*.js; keyring.js, transcripts.js and argon2-wasm.js on the desktop;
Kdf.kt, Keyring.kt and Transcripts.kt on Android. Everything else is
AGPL-3.0-or-later, which the RPM specs and package.json already declared
without a licence file to back them.
Two AGPL section 7 permissions:
- group applications may be under any licence when they use the interface
only through a named surface (static/licenses/APPLICATION-EXCEPTION.txt);
the reference application is 0BSD so that copying it brings no AGPL code;
- the Android application may be conveyed linked with Google Play services.
Third-party code is accounted for: THIRD-PARTY-NOTICES.txt is generated from
what a build ships (packaging/third_party_notices.py) for the deb/rpm venv and
the frozen Windows node — PyAV's wheel grafts in libx264 and libx265, which its
BSD licence does not mention — and the vendored browser libraries get their
licence texts and htm-preact.js its provenance. Wheels carry SPDX metadata,
RPMs %license, debs a DEP-5 copyright file, every Windows target LICENSE.txt.
test_licensing.py holds the line: the LGPL layer imports nothing under the
AGPL, the reference application nothing outside the application interface,
and every SPDX line is one of the known ones.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
| |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A cast button in Music's toolbar and in the music bar. With a television
chosen, each decrypted track goes to the relay with its cover — found as the
album card finds it — and plays there as music with its title, artist and
album; the bar's play, pause, seek, previous and next drive the receiver, its
clock is the receiver's, and the end of a track there moves the queue on.
A film or a photo taking the television pauses the bar; stopping the cast
carries the track on locally.
Photos and tracks now share one path: a whole file sent to the relay in
pieces (binary frames on Android, written to disk there), served at /file
with byte ranges and its cover at /cover, and loaded as what the relay says
it is. cast:image is gone; cast:chromecast:seek is new.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
A cast button in Videos' toolbar, at the top of Photos, in an album's bar and
in the lightbox, in a group and in Search alike. A television chosen there is
kept for the session: a film opened plays on it with the player as its remote
from the start, and a photo opened in the lightbox is shown on it, scaled to
1920x1080, upright, as JPEG. The lightbox gains a slideshow.
The relay serves one photo at /image behind the stream's token, on the desktop
and on Android; the shell, not the page, decides that the receiver loads it as
a picture.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
Held while a track plays or loads and released 5 s late, so skipping a
bad file with the screen off no longer drops the Android foreground
service, which cannot be taken back from the background.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
While a track plays, the page asks the shell to stay awake
(playback:keep-alive): on Android the cast's foreground service and
visible WebView, with a notification; on desktop a power save blocker.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
A transport failure while the page is hidden, or within 30 s of waking,
keeps the track and its spinner and retries once the page or the
connection is back, instead of skipping it with an error.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
| |
refusals
WebView now allows play() after the track fetch; a NotAllowedError leaves
the track waiting for the play button instead of raising an error.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
| |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
| |
Skip buttons with circular arrows, filled scrubber, large play/pause,
device header; the remote is its own component.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
| |
Shows the receiver's position with play/pause, ±30 s and a scrubber;
a seek restarts the relay where asked.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
| |
The player's remote mode reads where the television is instead of the
local playhead, which drifts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
|
| |
is missed
As the SDK recommends; and a connected session the listener did not hear of
still counts, so a missed callback no longer fails the cast.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
The node's first chunk can be the 28-byte ftyp alone, the moov in the next;
served as the header, the receiver had no moov and gave up. A receiver early
for the header now waits for all of it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
| |
Not debuggable, no WebView devtools, no console forwarding; installs over a
debug build and back. A stand-in until the release key (Stage D12).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
Fragments are 5-10 MB at a film's bitrate; dropped past 8 MB in memory, the
TV froze for their length. Each receiver now reads from its own spool file,
deleted with it; nothing is dropped short of a disk bound.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
Fragments dropped for a slow receiver, writes that block, a periodic per-client
summary, and every receiver state change with its position — the only trace a
freeze on the television leaves.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
| |
The desktop client's icon in the adaptive icon's safe zone, over its own edge
colour, so no launcher mask crops the M.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
| |
It keeps playing to pace the relay, so it doubled the television's sound.
The viewer's mute setting comes back when the cast ends.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
The init is what precedes the first moof; ports are reused like Node's; the
SDK is read on the main thread; a cast that fails says why on screen, and
success waits until the receiver actually plays. Never a VPN's address.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
They are the new stream, header first. Dropped, a cast relay restarted at the
landing got no ftyp/moov and the receiver gave up; they are now replayed in
order once reinitAt/resumeAt is done.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
A port of cast-relay.js (backlog also bounded in bytes), discovery and control
with the default media receiver, relay calls kept in order, and a foreground
service plus a WebView kept visible so a cast survives the screen going off.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
Native save over the Storage Access Framework and MediaStore, chunks sent as
binary bridge messages, a chosen folder that has gone asks rather than
redirects, unfinished files removed on abort and after a killed process.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
| |
pipelinedDownload kept every chunk's resolved promise until the end; a 2 GB
download held 2 GB in the page. Each is released once read.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
|
| |
Keystore-wrapped store, a Kotlin port of keyring.js and transcripts.js held
to the shared vectors, the same keys/device/secrets bridge as the desktop,
and a native confirmation before browser access is widened.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| |
|
|
|
|
|
| |
Gated on capabilities.nodeAdmin rather than on any bridge, so a phone with no
groups sees its invitations and the join link.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|