aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src
Commit message (Collapse)AuthorAgeFilesLines
* Player: redesigned cast remoteChristophe Besson10 hours13-72/+253
| | | | | | | 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>
* Player: remote control while castingChristophe Besson10 hours12-7/+248
| | | | | | | 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>
* Cast: receiver position and pause/play on both clientsChristophe Besson10 hours1-0/+13
| | | | | | | 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>
* fix(client): the local player is silent while castingChristophe Besson10 hours1-0/+18
| | | | | | | 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>
* fix(client): a seek's first segments are held while it lands, not droppedChristophe Besson10 hours1-1/+36
| | | | | | | | 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>
* fix(client): a download written to disk no longer holds the whole fileChristophe Besson10 hours1-0/+4
| | | | | | | 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>
* fix(client): the node setup welcome is for a build that has a nodeChristophe Besson10 hours1-1/+4
| | | | | | | 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>
* refactor(mnp): remove ten operator messages no client sent0.17Christophe Besson38 hours3-78/+2
| | | | | | | | | node_status, node_settings_set, roster_read, denylist_read, denylist_clear, node_reload and the signed gek_rotate, member_unpin, transfer_limits, group_detach leave MNP 6.0; the Node page and the CLI do this work over loopback. Their ops keep their tests, moved to the ops level. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(client): no confirmation dialog for adding a folder or its flagsChristophe Besson38 hours11-23/+2
| | | | | | | Removes confirmFolder (addRoot, attachGroup) and the writable confirmation added in e4f6177, with their two catalogue keys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(mnp): sharing a folder is decided on the node's machine only (MNP 6.0)Christophe Besson38 hours16-171/+90
| | | | | | | | | | root_add, root_update and group_attach leave MNP: adding a directory and switching writable/removable go through the loopback API (native dialog in the desktop app) or the CLI. The operator's Settings tab still lists the roots from any browser, read-only. The desktop app refuses to sign those ops; a loopback flag change now reaches open pages (publish_roots). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(client): list cast receivers as they answerChristophe Besson40 hours2-15/+47
| | | | | | | | The scan still runs six seconds, but the picker polls what it has found and shows each receiver immediately. A rescan no longer has its timer cut short by the scan it replaced. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(client): rotating a group key asks nothingChristophe Besson2 days11-12/+0
| | | | | | | Rotation narrows rather than widens: members still connected receive the new key, and nothing already shared changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(client): hosting a group asks nothing when its folder came from the pickerChristophe Besson2 days10-10/+0
| | | | | | | The folder chosen in the native picker is the consent; the dialog that followed on every group creation asked the same thing twice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(hub): a username is unique whatever its caseChristophe Besson2 days1-4/+10
| | | | | | | | Registration refuses a name that differs from an existing one only by case; accounts that already do keep their names, and a pending retry needs the exact name (F-26). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(hub): what an offer or a node message costs the hub is boundedChristophe Besson2 days2-25/+62
| | | | | | | | | An offer's IP-log row (kept a year) was written before any check, for any string named as a node; it is written once the offer goes to a node. The ICE list is capped (64 candidates, 32 KiB). A node's update_groups, a database read each, is budgeted like chat_notify and claims at most 1000 groups (F-22). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(client): the audit export never hands a spreadsheet a formulaChristophe Besson2 days2-5/+16
| | | | | | | A cell starting with = + - @ (or a tab or carriage return) gets a leading apostrophe; the export carries text members chose (F-29). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: remove a spike page served in production and an unused derivationChristophe Besson2 days1-265/+0
| | | | | | | | static/webrtc-test.html posted a raw password to /login from the hub's own origin; meshbay_common/keyderive.py derived keys from a password and nothing called it (F-32). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: an MBK2 bundle is opened once and stored again as MBK3Christophe Besson2 days3-4/+104
| | | | | | | | | | | | Transitional. The Argon2 run that makes M makes A, the key MBK2 bundles were sealed under; a session keeps it as a decrypt-only key (IndexedDB in a browser, the key storage in the desktop app). A client meeting an MBK2 bundle opens it — or its recovery copy — and stores the same identity as MBK3 once connected; the desktop app reseals or withdraws it as browser access says. A session without A asks for the passphrase once. Older formats stay refused by name. Replaces the unpin-and-reinvite step the 0.17 flag day required on every node. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: downloads are marked and keep their extension; Explorer files are refusedChristophe Besson3 days1-1/+6
| | | | | | | | | | | The desktop app writes the Mark-of-the-Web on each file it saves on Windows, as a browser does. Bidirectional controls are reserved characters in a saved name (portable-name.js and paths.sanitize_for_download, and again in the main process), so a name cannot display one extension and carry another. The node refuses uploads of files Windows Explorer acts on by itself: desktop.ini, .lnk, .url, .scf, .library-ms, .searchConnector-ms (F-19). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(hub): a stranger who knows your name locks only browsers you never usedChristophe Besson3 days4-17/+158
| | | | | | | | | | | A sign-in from a browser that presented no token is answered with one (known_browser, kept hashed, twenty per account); a later sign-in presenting it counts failures on its own row, which nobody else can spend. Passphrase checks inside an open session (change, e-mail, deletion, device, pepper) count on the account's own row, so a locked name no longer stops its owner there either; /me reports that row. Reset and erasure forget the browsers (F-15). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(hub): a group name fits its column and carries no control charactersChristophe Besson3 days2-4/+25
| | | | | | | | Over 128 characters was a 500 on PostgreSQL; line breaks, C0/C1 controls and bidi overrides are refused (joiners stay, for emoji). The creation form caps the field at 128 (F-13, what remains of it). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(node): how a hosted group admits people is the operator's, not the hub'sChristophe Besson3 days1-0/+3
| | | | | | | | | | | attach_group no longer copies join_policy and visibility from the hub's answer: they come with the operator's request (the desktop creation form, `group add --open`) and default to invite/private; the CLI says when the hub lists the group otherwise. Every string written into node.toml is escaped (toml_string) and read back through tomllib, so a group or folder name cannot write lines of its own (F-17). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: an identity signs a named kind, and a device approval answers a requestChristophe Besson3 days5-32/+99
| | | | | | | | | | | The desktop main process builds every transcript itself from fields (transcripts.js) and signs no raw bytes; the page's identity has the same contract (crypto.js transcriptFor). The keyring seals no bundle while browser access is off. On the node, device_add must redeem a pending request filed by the same keys, and device_revoke is signed under its own prefix (meshbay:device_revoke:v1), so a retirement signature admits nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(hub): the pepper and a device key take the passphrase, not a tokenChristophe Besson3 days5-27/+67
| | | | | | | | | POST /me/bundle-pepper (was GET) and POST /users/devices require auth_key. A refreshed or lifted token could otherwise fetch the pepper, or register a device whose every sign-in carries it. Both callers have just been given the passphrase. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: browser access, decided in the desktop applicationChristophe Besson3 days17-5/+194
| | | | | | | | | Off for an account made there: its identities stay on the device and nothing is left on nodes. Turned on from the Profile page behind a native confirmation; each node is settled when its group next opens. The hub keeps a mirror a browser reads to say why a group will not open; it grants nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(client): the desktop application keeps M and every node identity in its ↵Christophe Besson3 days19-59/+270
| | | | | | | | | | | | main process keyring.js derives, opens, mints, seals, signs and agrees there; the page gets public keys and a handle. Argon2 comes from the page's own WebAssembly build (Electron's crypto has none). Without OS key storage the page keeps its keys as a browser does. A node's bundle is settled after connecting, re-sealed when the key changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor(hub): the transport holds an identity, never a private keyChristophe Besson3 days10-96/+87
| | | | | | | | Two public keys, sign() and shared(); the apps take transport.signFn. What holds the keys (this page, or the desktop main process) is the identity's business alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: bump version to 0.17.0, and the client minimum with itChristophe Besson3 days2-3/+7
| | | | Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: bundles sealed per node under the passphrase and the hub's pepperChristophe Besson3 days20-203/+276
| | | | | | | | | The session key is M = HKDF(Argon2(passphrase) || pepper, account id); each node's bundle key and the playlist key derive from it. Bundles are MBK3, bound to account and node; MBK1/MBK2 are refused by name, never replaced silently. Playlists move to key v2 and are re-sealed over unreadable node copies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(hub): a bundle pepper per account, handed only to a proven sessionChristophe Besson3 days4-0/+102
| | | | | | | | Sealed at rest and bound to the account; returned by sign-in, device sign-in, a passphrase change and GET /me/bundle-pepper, never by a refresh, to a node token, in a token or in a log. Erasure clears it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(hub): a downloaded file opens in a tab only under a type that runs nothingChristophe Besson3 days3-20/+56
| | | | | | | Open is offered for PDFs, raster images, audio, video and plain text, typed from the name; HTML, SVG and the rest are not opened in the hub's origin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: the page connects to its own origin and reCAPTCHA, nowhere elseChristophe Besson3 days1-1/+7
| | | | | | | | connect-src drops https: and wss: in both policies. Checked against Google's reCAPTCHA test keys: in Chrome widget, token and registration unchanged; in Firefox the widget loads; no violation reported in either. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(client): the page names node operations, and the app confirms what ↵Christophe Besson3 days16-92/+149
| | | | | | | | | | | widens the node node:call is replaced by named operations with checked arguments; hosting a group, sharing an unpicked folder, key rotation, denylist clearing and a change of node account are confirmed by a native dialog. Every channel checks its sender, secrets:get/set/clear are gone, node:start writes the app's own hub. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(hub): the admin allow-list grants an account, not a usernameChristophe Besson3 days4-18/+148
| | | | | | | Each name in admin_usernames is pinned to the first active account seen holding it (admin_pins), so a name freed by a deletion grants nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: only the owner decides who hosts a group, and nobody is made a member ↵Christophe Besson4 days21-33/+731
| | | | | | | | | | | | | | | | | | | | | | | | | | unasked - hub: a node may host a group only if its account owns it or the owner approved that node (new `group_hosts`). Membership was the ceiling, and every member holds the group key, so any member's node could register as a host and be the one clients kept. A node claiming a group it may not host is recorded as a request; the owner is notified once and approves or refuses it (GET/POST/DELETE /v1/groups/{id}/hosts[/{node_id}]), which takes effect on a connected node at once. - hub: an owner adding a username creates an invitation (new `group_invitations`), accepted or declined by the invitee (/v1/groups/invitations, /{id}/invitation/accept|decline). Until then the group is not listed, not dialled, not searched and not in any token. Invitation links, open joins and group creation still make members directly: they are the account's own act. - hub: the MNP token names only the group it is minted for (group_id is now required), so a node operator no longer learns a member's other groups. - SPA: invitations on the home page; invited people and host requests in the group's settings; the transport sends group_id. Ten catalogues. - Browser probes for both screens, run in Chrome and Firefox. - Design §5.2, §7.2, §7.3, AV32, AV33; protocol §6.3; user guide. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: a member can no longer lock a node, crash it with a link, or stop hub ↵Christophe Besson4 days1-35/+90
| | | | | | | | | | | | | | | | | | | cleanup - node: only a wrong code counts towards the join lock, now per account (5) as well as node-wide (20), and it is consulted only when a code is tried. Every member reconnecting gets the group key through join_request, so a lock checked before recognition let one member refuse it to everyone. - node: link previews read the body as a stream and stop at the cap, counted on decoded bytes; a declared oversized image is not read; 15 s total deadline; image decoding off the loop. `client.get` had buffered the whole (decompressed) response before the caps looked at it. - hub: the daily purge of never-verified accounts detaches their IP-log rows (keeping the name) and clears every other reference first, and each cleanup step runs on its own. On PostgreSQL the bare DELETE violated the ip_logs foreign key and stopped every purge behind it for good. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(hub): search title, description, canonical and noscript in the shellChristophe Besson4 days1-3/+30
| | | | | | | | | A descriptive <title>, a two-line meta description apart from the short one messengers get, "/" canonical for every shell path, and a <noscript> pitch with the download and doc links for crawlers that do not run app.js. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(hub): sitemap.xml, named by robots.txtChristophe Besson4 days2-0/+22
| | | | | | | Home, downloads, and the repository's about page and docs on git.meshbay.org, spelled as the welcome page links them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor(hub): remove the closed relay registryChristophe Besson5 days2-168/+0
| | | | | | | Every /v1/relays route answered 503 and nothing called them; no TURN relay is needed. The proof-of-possession rule it carried stays as AV6. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: keypair_bundle_delete is reserved for device_policy (O3)Christophe Besson5 days1-0/+4
| | | | | | | The node honours it and no interface sends it; offered alone it would strand the next browser that signs in. Stated in both documents. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor: drop the unread transfer_limits field from the handshake ackChristophe Besson5 days1-9/+0
| | | | | | | The interface reads a member's cap from transfer_state and never read the copy on the ack. The transfer probe reads it from transfer_state too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(client): save files under a name every platform can writeChristophe Besson5 days15-6/+113
| | | | | | | | | A node serves the name its disk gave a file; the client now makes it portable at save time (single file, zip entries, zip name) and says so on the transfer row. Same rule as paths.sanitize_for_download, held by a parity test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(hub): reports from public-group members, decided by an administratorChristophe Besson5 days20-71/+745
| | | | | | | | | | A report needs a person's account at least a day old, membership of the public group, and fits a daily allowance per account. Past the threshold a hash is queued and administrators are notified; blocking without review is an instance setting, off by default. Report menu item in public groups, Reports tab and settings in the admin panel. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: nodes apply the content blocklist in their public groupsChristophe Besson5 days3-36/+72
| | | | | | | | | A node hosting a public group syncs the hub's blocklist on every connection (paged, node token only) and applies pushed changes. A blocked file leaves the index and is refused (content_blocked); private groups are untouched. The unused per-hash check route is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor: remove the public-content swarmChristophe Besson5 days5-131/+40
| | | | | | | | Nodes registered the hashes of their public groups on the hub and nothing ever read them back. Routes, model and node registration removed; a migration drops swarm_sources. No node sends the hub a content hash now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: bound pending admin challenges and sign every value an op acts onChristophe Besson5 days4-15/+62
| | | | | | | | | | Any member could make a node hold unbounded challenge requests; a connection now keeps at most 8, 64 KiB each. root_add, group_attach, invite_create and tmdb_config signed less than they did; their subjects are now canonical JSON of every value (the TMDB token by SHA-256). MNP 5.0, floor kept at 4.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor: remove dead code across packagesChristophe Besson5 days14-280/+10
| | | | | | | | | Unused modules, functions, constants and client helpers with no caller, the unreachable hub:probe IPC handler, and the CSAM hash matching. Behaviour unchanged; the dispatch golden loses only the two removed message types. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: refuse an unsigned handshake challengeChristophe Besson5 days1-25/+16
| | | | | | | | Every node the 4.0 floor admits signs its challenge, and one without a channel binding could not complete the proof anyway, so a missing signature is refused like a wrong one (browser and QUIC client). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(hub): new login tagline, dimmer gradient topChristophe Besson6 days11-11/+11
| | | | Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(hub): say so when a lazily loaded view cannot be fetched0.16Christophe Besson6 days13-5/+66
| | | | | | | | A tab opened before a hub deploy got 404 for every module it had not loaded yet, and lazy.js kept its spinner for good. It now shows a notice with a Reload button and logs the failure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>