aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub
Commit message (Collapse)AuthorAgeFilesLines
* test(hub): pin the Android Sync probe to EnglishHEADmainChristophe Besson14 hours1-1/+3
| | | | | | | The probe followed the browser's language, so its assertions on English labels failed on a machine set to French. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: keep the photo lightbox's close button on a phone screenChristophe Besson40 hours5-40/+81
| | | | | | | | | The bar was 457 px wide and pushed the close button to 397..437 px, off a 360 px phone. Delete leaves the lightbox (it stays on the tile's menu: right-click, or press and hold), and below 768 px the bar drops the file name, the zoom label and 100%. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: keep the sidebar for groups; search in the nav, the rest in the user menuChristophe Besson2 days16-88/+186
| | | | | | | | | | | | | | | | | | | | | | | Search is an icon in the nav, left of the tray button and the bell, and marks itself current on its page. Administration, Node, Create group and Android Sync moved to the user menu, between Settings and the theme, each under the condition it had in the sidebar. The sidebar keeps Public groups and the groups. On a phone, a tablet or the Android application the menu trigger is the avatar alone (max-width 768px or a coarse pointer); the name is the dropdown's first line, one line with an ellipsis. Measured: a 20-character handle put the trigger 159 px off a 320 px screen with search and transfers in the nav, and a 64-character one pushed the dropdown 122 px off the left. Creating a group is a desktop-application feature by design: a hand-typed #/create-group no longer opens the form in a browser, and QUICKSTART step 6 now says to use the desktop application instead of the browser. test_layout_responsive read only the first 768px media block; it reads them all now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: let the operator pause and resume a root's indexing (MNP 6.2)Christophe Besson2 days18-25/+208
| | | | | | | | | | | | | | | A whole-root walk now checks its root before every file: removed or ejected, it stops instead of reading the drive to the end; paused, it finishes from the hash cache alone. A paused root is not watched, not swept by reconcile and survives a restart (roster), its indexed files stay served. Resume runs the reconcile sweep over the root, so entries and their enrichment are kept. Signed root_pause / root_resume over MNP, loopback routes, CLI `root pause|resume`, a pause button on the indexing dock and pause/resume in the roots table of Group settings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(hub): tell the person at sign-up what the hub keeps and whyChristophe Besson2 days14-10/+44
| | | | | | | | | | | | | | | | A line under the Register button: creating an account accepts the hub's terms of use, and the law requires the hub to keep the connection log (IP address, date, action) for one year, even after the account is deleted. It links to the legal pages of the hub in use, so a self-hosted hub shows its own operator's documents. A notice, not a checkbox: the account rests on the contract and the log on a legal obligation, so there is no consent to ask for, and one that blocks sign-up would be neither free nor withdrawable. legalUrl() moves to platform.js, shared by the sidebar and the form. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(node): carry connections on several coresChristophe Besson2 days12-0/+68
| | | | | | | | connection_processes (default 1) starts that many processes for the WebRTC stack; sessions and keys stay in the node. Set from node.toml or the Node page, applied to new connections. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: spread a large download over eight connectionsChristophe Besson3 days4-3/+404
| | | | | | | | | One WebRTC connection carries 5 to 6 MB/s whatever the line can do. A leased download of 64 MB or more now opens seven more to its node, and a lease serves the other connections of its device in its group. Measured through the whole stack: 5 MB/s before, 24 after. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(android): what the first backup on a phone showedChristophe Besson4 days20-28/+93
| | | | | | | | | Slices are asked for in the address, not a Range header the WebView drops; a settings change runs at once; the notification is updated once a second; the SMS section says how to lift Android's restricted setting; the photo section says whether videos are included. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(android): back up WhatsApp's own chat backups, and its media if askedChristophe Besson4 days21-15/+343
| | | | | | | | A WhatsApp section takes WhatsApp's folder through the picker, sends its encrypted Databases and Backups (not the dated copies of earlier weeks) into <folder>/<account>-whatsapp, and names the restore set in the manifest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(android): send a manifest of what each backup run sentChristophe Besson4 days4-4/+66
| | | | | | | | Photos, videos and files record, per file, their path on the node, their path and album on the phone, dates, size and SHA-256, uploaded as meshbay-manifest/manifest-<date>.jsonl once the run is done. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(android): back up the files of folders the person choosesChristophe Besson4 days24-221/+735
| | | | | | | | A Files section takes folders through the system picker (no storage permission), refuses DCIM, Pictures and Movies, skips what the photo backup sends, and runs on the photo backup's own runner into <folder>/<account>-drive. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(photos): show a phone's clips in their albumsChristophe Besson4 days14-9/+354
| | | | | | | | Videos in a photo folder are thumbnailed by the node, counted and marked in the album, and played in the group's video player from the lightbox; the slideshow passes them by. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(android): back the videos of the chosen albums up beside the photosChristophe Besson4 days15-22/+150
| | | | | | | | A 'Videos too' option, off by default, sends them into the same YYYY/YYYY-MM folders. Files are read from the phone a ranged chunk at a time, so a large video is never whole in the page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(android): back the calendars up as an iCalendar fileChristophe Besson4 days17-14/+65
| | | | | | | A Calendar section sends every calendar the person can edit, as a dated .ics, into <folder>/<account>-calendar once a day when it changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(android): back up the personal profile onlyChristophe Besson4 days1-0/+14
| | | | | | | A copy of the application inside a work profile offers no backup, and no source reads another profile. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(android): back text messages up, in a build Play does not getChristophe Besson4 days18-15/+104
| | | | | | | | A Messages section sends the SMS added since the last copy, as restorable <smses> XML, into <folder>/<account>-messages/YYYY. A play flavor has neither READ_SMS nor the code that reads messages; full is the default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(android): one backup destination, a group the account owns aloneChristophe Besson4 days24-466/+645
| | | | | | | | Chosen once at the top of Android Sync for every kind; photos and contacts go into <folder>/<account>-photos and -contacts. Owner and sole member are checked at set-up and before every run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(hub): delete a photo from the Photos appChristophe Besson4 days3-8/+394
| | | | | | | On a right-clicked tile and in the lightbox bar, after a confirmation, for the node's operator or the photo's uploader, as in Files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(android): back the phone's contacts up to a group of one's ownChristophe Besson4 days20-17/+1016
| | | | | | | | A Contacts backup section on the Android Sync page sends a dated .vcf into <folder>/<account>-contacts once a day when the address book changed, only to a group the account is alone in, checked again at every run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(hub): photo backup moves to its own Android Sync pageChristophe Besson4 days19-17/+65
| | | | | | | A Phone section of the side menu leads to it, on the Android application only; Settings no longer holds it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(hub): let the cast relay script end by itselfChristophe Besson4 days1-1/+3
| | | | | | | | | | | test_cast_subtitles.py's node script called process.exit(0) right after relay.stop(), with fetch's sockets still closing. Node 24 on Windows aborted there on a libuv assertion (UV_HANDLE_CLOSING, src\win\async.c) two runs in three, after printing its results, so the module fixture failed and seven tests errored at setup. Nothing is left once the relay stops: the script ends by itself, in the same 0.1 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(android): back photos up under YYYY/YYYY-MM, not YYYY/MMChristophe Besson4 days2-7/+7
| | | | | | A month folder named 08 alone read like an album number. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(hub): wait for a resumed transfer's state, not for 120 msChristophe Besson4 days1-4/+15
| | | | | | | | | | | | | The pausable run in test_transfers.py moves in setTimeout(5) chunks, and the resume test gave it a fixed 120 ms to finish. A setTimeout(5) lasts about 15 ms on Windows' default timer, so the resumed run took 156 ms there and the test failed with nothing wrong; the 20 ms wait before pausing had the same thin margin. The run now records how far it got (state.at), and the test pauses after two chunks and waits for "done", bounded at 2 s. The assertions are unchanged, and a resume that restarts from zero is still caught (checked by introducing one in transfers.js). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(client): say when the account's node is elsewhere, and let go of itChristophe Besson4 days14-3/+284
| | | | | | | | | | | | | | | | | | | | | | Signing in on a desktop links this machine's node to the account, but never over a key already linked (that would cut off the user's other machine) nor a node set up for another account. On a real install both left the node reading "Running" while the hub refused it in a loop, and nothing said why. And the only way to unlink was the linked machine's own Node page, of no use once that machine is gone. - The Node page works out, from the node and the hub each time it looks, whether this node can serve the signed-in account (nodeLinkProblem), and says why not: "Link this node instead" (asked first) puts this node's key on the account; "Use this node for my account" switches a node set up for another account through node:start, which takeOver now lets past a node that answers "running". The first version went through node:start for both, and clicking it on a real install did nothing: a node signed in before the account was linked elsewhere answers "running". - The Profile page unlinks the account's node (DELETE /v1/users/me/node_key), from any machine. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(packaging): the Store package declares what the NSIS scripts doChristophe Besson4 days11-6/+23
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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>
* feat: back up the phone's photos to a group, once a day on Wi-FiChristophe Besson4 days24-7/+1655
| | | | | | | | | | | | | | | | | | | | | | | | | The Android application sends the photos taken on the phone to one folder of one group chosen by the member (docs/MESHBAY_DESIGN.md §9.12). The phone lists MediaStore, keeps a ledger of what was sent and hands each photo's bytes to the page by an opaque token on the packaged origin; the page decides when a run is due and uploads through the existing path, one photo at a time under a slot. - Once a day from the last finished run, on an unmetered network only; "Back up now" asks first on mobile data. Leaving Wi-Fi stops after the file in flight. - Photos already on the phone are sent by default, newest first, under <folder>/YYYY/MM; edits are sent beside the original as -edited-<date>. - Additive by construction: nothing is ever deleted, renamed or replaced on the node, and a photo deleted on the node is not sent again. - A confirmation names the group, owner, members, folder and size when the destination or starting point changes; a lasting refusal (disk full, folder read-only or gone, no longer a member) is said once and retried a day later. - No ACCESS_MEDIA_LOCATION, so the platform redacts photo locations. - A dataSync foreground service keeps a run going with the screen off. HEIC/HEIF photos are sent but not shown in Photos yet (§15.2). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(node): refuse an upload on a full disk with a stated reasonChristophe Besson4 days13-2/+19
| | | | | | | | | | | | Nothing on the upload path knew about ENOSPC: a write that found no room raised out of the handler, the catch-all answered "Request failed", and the .part stayed behind holding the space that had run out. The node now refuses with `disk_full` at chunk 0 when the announced size would leave less than 1 GiB free, and at any write that fails with ENOSPC/EDQUOT, dropping the partial. The client carries the code on the error and the transfers panel says "The node's disk is full" in every catalogue. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(hub): an open application looks for new notifications when you come backChristophe Besson5 days2-0/+77
| | | | | | | | | | | | | | The page asked for the notification list at sign-in and at a token renewal, and at no other time. A notification created after the application started, such as a chat line the hub wrote 20 ms after the message, stayed unseen until the next launch. The hub has no channel to the page and is not polled on a timer, so the list is asked for again on a gesture: the application returning to the foreground (an Android phone included) and the home page, where the list is shown. Two requests less than 30 s apart count as one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: let the operator purge a group's chat (MNP 6.1)Christophe Besson5 days15-3/+80
| | | | | | | | Signed chat_purge from the Chat settings deletes every stored message; epoch keys and attachments stay. The ack is broadcast so open chat panels empty. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: notifications on Android while closed, with nothing to installChristophe Besson5 days24-5/+1267
| | | | | | | | | | | | | | | | | | | | The phone fetches what is new every fifteen minutes with a poll secret (POST /v1/push/poll) that reads notification lines and nothing else. When a UnifiedPush distributor is already installed, the hub also pushes at once, encrypted to the phone (RFC 8291); losing the distributor falls back to fetching. The hub now honours "disable all notifications" itself: create_notification creates nothing for that account, as it already did for a muted group, so neither switch lets anything reach a phone. The interface used to be the only reader of the account-wide switch. Push endpoints are member-supplied URLs: a send refuses non-public addresses, connects to the address it checked, and follows no redirect. Android build untested here (no SDK on this machine). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: sign Android releases with the release keyChristophe Besson6 days1-0/+11
| | | | | | | assembleRelease reads the key from ~/.gradle/gradle.properties and fails without it instead of falling back to the debug key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(hub): re-read the roster before saying a key changedChristophe Besson6 days4-20/+194
| | | | | | | | A member invited after the roster was read showed "key changed" on each message until a reload. Read it again once per account and device on the connection, shared by concurrent messages, and pin only the final verdict. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: bump version to 0.19.0Christophe Besson6 days3-3/+3
| | | | Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: set the Windows node up at sign-in, and stop it for realChristophe Besson6 days14-12/+81
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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>
* test: read UTF-8 explicitly in the licensing and keyring-vector testsChristophe Besson6 days1-1/+1
| | | | | | | | | | 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>
* fix(node): name members admitted without an invitation nameChristophe Besson6 days3-2/+24
| | | | | | | | | | | 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>
* fix(hub): keep "add a directory" when one node check is missedChristophe Besson6 days2-13/+125
| | | | | | | | 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>
* fix(hub): let a download wait out a reconnect instead of failingChristophe Besson7 days3-7/+127
| | | | | | | | 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>
* fix(hub): move "clear finished" into the finished group's headChristophe Besson7 days3-16/+46
| | | | | | | | 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>
* fix(hub): stop the transfers panel scrolling sidewaysChristophe Besson7 days1-1/+7
| | | | | | | 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>
* fix(hub): play video on iPhone through ManagedMediaSourceChristophe Besson7 days11-2/+24
| | | | | | | 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>
* feat(hub): say "N groups unreachable" on Search for a few seconds, not for good0.18Christophe Besson9 days7-34/+86
| | | | | | | | | | | | 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>
* test(hub): join from an invitation link and land under the group's handleChristophe Besson9 days2-5/+35
| | | | | | | | | | | 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>
* feat: copy a file's or folder's #/name@owner link from Files, Music, Photos ↵Christophe Besson9 days21-20/+560
| | | | | | | | | | | | | | | | | | | | | | | 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>
* feat: open a group, a folder or a file from a #/name@owner linkChristophe Besson9 days18-11/+615
| | | | | | | | | | | | | | | | | | | 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>
* fix: list as members only the accounts the node has admittedChristophe Besson9 days13-6/+55
| | | | | | | | | | | | | | | | 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>
* feat: move the desktop client's Hub section from Settings to ProfileChristophe Besson9 days12-35/+35
| | | | | | | | | | | 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: generate an HTTP API listing for the hub and the node control APIChristophe Besson9 days22-2/+85
| | | | | | | | | | | | | | | | | | | 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>
* feat: prefill https://meshbay.org on the desktop client's hub screenChristophe Besson9 days12-15/+4
| | | | | | | | 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>
* fix: read the node token per call and detect before polling the index dockChristophe Besson9 days1-0/+10
| | | | | | | | | | | | | 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>