<feed xmlns='http://www.w3.org/2005/Atom'>
<title>meshbay.git/packages/meshbay-hub/src/meshbay_hub/static/style.css, branch 0.20</title>
<subtitle>MeshBay — read-only public mirror</subtitle>
<id>https://git.meshbay.org/meshbay.git/atom?h=0.20</id>
<link rel='self' href='https://git.meshbay.org/meshbay.git/atom?h=0.20'/>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/'/>
<updated>2026-10-15T12:25:00Z</updated>
<entry>
<title>feat(auth): the recovery key never reaches the hub</title>
<updated>2026-10-15T12:25:00Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-10-15T12:25:00Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=28c77b7648b2a44a18be64ec44344d91cccfd742'/>
<id>urn:sha1:28c77b7648b2a44a18be64ec44344d91cccfd742</id>
<content type='text'>
Ticking "also e-mail it to me" at registration sent the recovery key R in
the register body, and the hub appended it to the verification e-mail: the
hub saw R in clear on its way out, and the resend door — no captcha — let
anyone who knew a pending username and address have the hub mail a
"recovery key" of their choosing.

The box is gone. The recovery screen offers "E-mail it to myself", a mailto:
link that opens the person's own mail application with the key filled in;
the desktop shell hands mailto: to the system (http, https and mailto only)
and the Android shell to a mail application (ACTION_SENDTO). The CLI drops
--email-recovery-key and prints the key, as before. The hub mails the code
alone, and refuses a register that still carries a recovery_key rather than
dropping it, so an older client offering the option cannot tell someone
their key was sent when it was not.

A mailbox holding R remains, with the reset code that reaches the same
mailbox, enough to recover the account's identities; the link says so, and
it is the person's choice.

Checked in Chrome and Firefox (headless, the real page against a stubbed
hub) and by the CLI's end-to-end registration against a local hub. The
Android change was not built here, and the desktop link was not clicked in
the application.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat(hub): one account, several linked nodes</title>
<updated>2026-10-15T07:33:28Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-10-15T07:33:28Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=1e8eae1f3de765fcd8163716de6e5fe5aab5a034'/>
<id>urn:sha1:1e8eae1f3de765fcd8163716de6e5fe5aab5a034</id>
<content type='text'>
An account links up to MAX_NODES_PER_ACCOUNT node keys (node_keys table)
instead of the one users.pk_node_ed25519; the migration carries each
account's key over and the downgrade keeps the last one linked.
/v1/nodes/auth accepts any linked key, and its token names the key that
won it (nk). PUT /me/node_key adds a key, idempotently, 409 past the
limit; DELETE /me/node_key names the key: a browser session any of its
own, a node only its own. Unlinking a key closes that node's socket and
refuses its tokens at once, on REST and on the socket; the account's other
nodes are untouched. /pubkeys no longer gives a node key to other accounts,
and gives the caller its last linked one, which installed desktop apps read.

The node reports waiting_for_node_key while its key is unlinked and signs
in again as soon as the hub says so, so a key linked again is back within
seconds; unlinking itself signs in first to hold a token naming its key.

Profile -&gt; Linked nodes lists each node (connected, last seen, IP, linked
since, This machine in the desktop app) with Copy and Unlink, and adds a
key by hand. The Node page banner, Create group and the desktop app add
this machine's key instead of replacing another's. Messages that pointed
at Settings -&gt; Link Node point at Profile -&gt; Linked nodes.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat(node): skip a file the disk stops answering for, then pause its root</title>
<updated>2026-10-14T08:17:49Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-10-14T08:17:49Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=80ac882346b474bb7ea4e8c8afd4694bd13dd9dd'/>
<id>urn:sha1:80ac882346b474bb7ea4e8c8afd4694bd13dd9dd</id>
<content type='text'>
A Windows tester's scan stuck for good on one directory of a large disk:
one read that never returned held the indexer's only thread, and with it
every root behind that one.

- A disk call is timed by its silence, not its length: each directory
  entry listed and each megabyte read counts as an answer. After 60 s of
  silence the call is abandoned on its daemon thread, the file is noted
  unread and the scan goes on.
- Three silent files in a row pause the root (not persisted). Resume
  skips the files already known to give no answer; a restart, a replug
  or a change to the file retries them.
- The walk is per directory with os.scandir: a subdirectory that cannot
  be listed costs itself, and a listing that stops answering pauses the
  root at once. A scan no longer stats anything from the event loop.
- The log names the file after 10 s of silence, the indexing band shows
  it, and Group settings shows the paused root, where it stuck and the
  unread files with their reason, on the node's machine only.
- generate_http_api.py writes LF on Windows too.

Verified with real kernel-blocked opens (an unacknowledged oplock) in the
installed frozen app.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>fix: keep the photo lightbox's close button on a phone screen</title>
<updated>2026-10-12T10:57:49Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-10-12T10:57:49Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=13e7980071ab11b07b5d473e43506c6cb4ab637e'/>
<id>urn:sha1:13e7980071ab11b07b5d473e43506c6cb4ab637e</id>
<content type='text'>
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 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat: keep the sidebar for groups; search in the nav, the rest in the user menu</title>
<updated>2026-10-11T23:27:00Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-10-11T23:27:00Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=3324b4eb701e34f64e859dd90937793ccf7f75c5'/>
<id>urn:sha1:3324b4eb701e34f64e859dd90937793ccf7f75c5</id>
<content type='text'>
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 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat: let the operator pause and resume a root's indexing (MNP 6.2)</title>
<updated>2026-10-11T22:17:32Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-10-11T22:17:32Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=14911aae21ec4975f7a805b91e3c4867cb41974b'/>
<id>urn:sha1:14911aae21ec4975f7a805b91e3c4867cb41974b</id>
<content type='text'>
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 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat(hub): tell the person at sign-up what the hub keeps and why</title>
<updated>2026-10-11T21:43:35Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-10-11T21:43:34Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=f6f107e02c7a4aa7326c9a82310a239327d81ed6'/>
<id>urn:sha1:f6f107e02c7a4aa7326c9a82310a239327d81ed6</id>
<content type='text'>
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 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat(photos): show a phone's clips in their albums</title>
<updated>2026-10-10T13:26:45Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-10-10T13:26:45Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=1521cc1ac0cca7ee8786056f84fa399d5cc268f9'/>
<id>urn:sha1:1521cc1ac0cca7ee8786056f84fa399d5cc268f9</id>
<content type='text'>
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 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat: back up the phone's photos to a group, once a day on Wi-Fi</title>
<updated>2026-10-09T16:23:56Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-10-09T16:23:52Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=25152ddb61a89ea3ea29d3aef5de13343429d32a'/>
<id>urn:sha1:25152ddb61a89ea3ea29d3aef5de13343429d32a</id>
<content type='text'>
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
  &lt;folder&gt;/YYYY/MM; edits are sent beside the original as -edited-&lt;date&gt;.
- 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 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>fix(hub): move "clear finished" into the finished group's head</title>
<updated>2026-10-07T09:38:39Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-10-07T09:38:39Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=219093c823494bc8d760c99a7a55c4a23059abb5'/>
<id>urn:sha1:219093c823494bc8d760c99a7a55c4a23059abb5</id>
<content type='text'>
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 &lt;noreply@anthropic.com&gt;
</content>
</entry>
</feed>
