<feed xmlns='http://www.w3.org/2005/Atom'>
<title>meshbay.git/packages/meshbay-node/tests/test_audio_root_policy.py, branch 0.15</title>
<subtitle>MeshBay — read-only public mirror</subtitle>
<id>https://git.meshbay.org/meshbay.git/atom?h=0.15</id>
<link rel='self' href='https://git.meshbay.org/meshbay.git/atom?h=0.15'/>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/'/>
<updated>2026-09-10T15:49:58Z</updated>
<entry>
<title>refactor(mnp)!: one operation for an app's folders, not one per app</title>
<updated>2026-09-10T15:49:58Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-10T15:49:58Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=07ff8b4f6143039fcc74b8cf7c423282bce093c1'/>
<id>urn:sha1:07ff8b4f6143039fcc74b8cf7c423282bce093c1</id>
<content type='text'>
`video_root`, `audio_root` and `photo_roots` are gone — the messages, the
signed operations, the handlers, the `ops` wrappers, the three scalars on the
handshake ack, and the client's handlers for their acks. `app_directories`
does the same thing for every application, keyed by the app's own registry
name, and it is what the SPA has been sending.

The three were the same instruction three times, differing only in the key they
wrote and whether they carried a string or a list. That shape is what made
adding an application mean adding a message type, an op, a handler and a widget;
it also meant three validation paths, and the older ones validated nothing —
a typo was stored and then quietly matched no entry, an app showing an empty tab
with no way to tell "misconfigured" from "no files yet".

**What stays, and why.** `Roster.LEGACY_DIR_KEYS` still reads `video_root` and
friends out of `group_settings`: that is a key on an operator's disk, not on the
wire, and a node upgraded into this must find its own configuration. The Search
page still reads its own older cache keys, for the same reason — the cache
outlives a deploy. `CTX_ALIASES` keeps only `chat`, which is the one app whose
second name something still reads.

The two per-app policy test files go with the messages. What only they held —
the real challenge/response path from message to database, which no other test
exercises — is retargeted at `app_directories` in
`test_app_directories_signed.py`, and the handler's own refusals (unknown app,
malformed `directories`, nobody to authorize it) join `test_app_directories.py`.

Node and common suites 1368 passed.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01AsoWC3GmhNdwVFomW3QjH3
</content>
</entry>
<entry>
<title>feat(node): Phase 2 server side — one directory setting for every app</title>
<updated>2026-09-06T16:16:57Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-06T16:16:57Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=85a2ec47b7ad334208a3dbb091fadccc7631785c'/>
<id>urn:sha1:85a2ec47b7ad334208a3dbb091fadccc7631785c</id>
<content type='text'>
`video_root` (a string), `audio_root` (a string) and `photo_roots` (a list)
said the same thing three ways: three roster accessors, three ops, three MNP
messages, three admin-op subjects. They become `set_app_directories(app_key,
paths)` and its single-directory wrapper, stored under `&lt;app&gt;_directories` and
keyed by the app's registry name — so an application can be added without
touching this layer, which is the whole claim of the plugin architecture.

The three old names still work. Their MNP messages are handled, and the roster
falls back to the old key when the new one is unset, so a node upgraded into
this keeps working with no migration step — the plan called for a script, and
a script nobody runs on the machine where it matters is worse than a fallback.

Two things are new rather than moved:

The paths are validated. The setters this replaces accepted anything, so a
typo — or a path left behind when a root was removed — was stored happily and
then matched no entry, leaving an app showing an empty tab with nothing to
distinguish "misconfigured" from "no files yet". Deliberately not
`RootSet.resolve()`: that also refuses a currently-unavailable root, and an
operator must be able to point an app at a library on a drive they ejected.

The legacy scalar is derived, never stored. `video_root` still rides on the
handshake ack for MNP 1.0 clients; kept as a second stored value it would
drift from the list within one run, which reads as "it works after a restart".

Also here: chat's own two settings (a directory, which must be on a read-write
root because it is a destination rather than a view, and a link-preview switch
gating the unfurl path — checked before the cache, or turning it off would
still serve every preview already fetched), the `app_directories`,
`chat_directory` and `chat_link_preview` MNP messages, the plural
`&lt;app&gt;_directories` on the handshake ack, and `music` as the app's one
identifier where storage said `audio` and the registry said `music`.

The Music enricher now resolves a boundary per configured directory rather
than one for the group: with several, a single boundary is wrong for all but
one of them, and for Music that is the difference between reading a folder as
an artist and reading it as a release.

Suite: 11 failures, all pre-existing.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_011pvMdvLBG92jyhvD5pD6us
</content>
</entry>
<entry>
<title>test(node): a real signed audio_root save through the full challenge path</title>
<updated>2026-08-24T21:34:57Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-08-24T21:34:57Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=aeeaf0d537a1942010caf222b72333841b80b7f8'/>
<id>urn:sha1:aeeaf0d537a1942010caf222b72333841b80b7f8</id>
<content type='text'>
Every existing audio_root test either called ops.set_audio_root directly
or mocked out _issue_admin_challenge — none of them exercised real
signature verification, _do_admin_response, or the shared groups_ctx/
roster wiring _run_op depends on. Worth ruling out a break somewhere in
that real path specifically: a report described a save that looked like
it worked (the Music tab showed content right after) not surviving a
reload.

Drives the real _do_audio_root -&gt; admin_challenge -&gt; sign -&gt; _do_admin_response
-&gt; _admin_exec_audio_root path with a genuine Ed25519 operator key, then
opens a *separate* Roster instance against the same db file — the direct
question a "worked, then reverted" report raises: does the value actually
land durably, in a form any later connection reads back correctly. It
does; this passes. The one thing missing from the session fixture to get
this far was peer-registry self-registration (a real session adds itself
on handshake completion — without it, the final ack has nowhere to go,
including back to the requester).
</content>
</entry>
<entry>
<title>feat(node): add audio_root, gate Music enrichment on it like video_root</title>
<updated>2026-08-24T20:24:52Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-08-24T20:24:52Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=665fb2004e55b72ea483aa50fea81f6a6fd9c322'/>
<id>urn:sha1:665fb2004e55b72ea483aa50fea81f6a6fd9c322</id>
<content type='text'>
musicbay.md's original call — Music needs no root, tag reads are cheap so
just cover the whole shared tree — didn't hold up against a real messy
library: everything under every shared folder got mixed together with no
way to scope Music down to an actual music collection. This adds an
audio_root setting, symmetric to video_root in every respect: signed
operator op (audio_root/audio_root_ack, MNP bumped to 0.10), validated
against a real directory in the group's own roots before a signature is
even asked for, gates tag/cover enrichment exactly like video_root gates
ffprobe/TMDB (nothing runs until it's set, only files under it once it
is), and a set/change fires a one-off sweep of whatever the folder already
contains.

The old trigger — sweep everything the instant "music" joins enabled_apps
— is gone along with the root-less design it belonged to; setting
audio_root is now the trigger, mirroring set_video_root's
enrich_video_root_fn exactly.

Test coverage mirrors the video_root suite: policy (refuse before a
signature round trip, accept/store correctly) and the enrichment gate
itself (nothing without a root, only files under it, sweep on set).
</content>
</entry>
</feed>
