From 20a824118c09af15d6c338db4c9480ffe5cbcdb6 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Sat, 19 Sep 2026 02:12:47 +0200 Subject: docs: cite MESHBAY_DESIGN.md and a section instead of the merged notes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The per-feature design notes were merged into docs/MESHBAY_DESIGN.md and deleted from the tree on 2026-09-11, but ~230 comments across the three packages still named them — usually written `docs/musicbay.md §3.2`, as though the file were still in docs/. A reader had to know §16 existed to resolve any of them. They now name the section directly. Every mapping comes from §16, the concordance, which already records where each old section landed: musicbay -> §9.8, mediacenter -> §9.7 for the Videos app and §6.5 where the subject is derived data, photos -> §9.9, auth-confirm -> §3.6, refactoring-search -> §9.11, invite-pairing-v1 -> §3.4, per-node-identity-v1 -> §3.2, captcha -> §7.7, chat-sender-keys -> §4.5, apps/refactor-groups -> §9.1–§9.4, desktop-client-v1 per section. Bare citations of the same documents (`draft-v6 §2.11`, `§4.8`, `§3.4`) are retargeted too: those collide with real section numbers in the design document, so leaving them would have been worse than the named form. Four cases the concordance does not cover, each decided rather than guessed: Sub-item references into documents that no longer exist — mediacenter's `§3.3 row 4`, `§3.4b/c`, `apps.md §3b` — name rows and sub-items §9.7 and §9.2 do not reproduce. The module-level citation stays; the sub-item pointer is dropped. The V-findings keep their labels but lose the dead `§10.1/` prefix. §13.8 lists V1–V13 as per-application open items, which is not what the labels mean in these comments, so pointing them at §13.8 would have been a false citation. `apps.md §5`'s virtualization requirement has no counterpart anywhere in the design document. The requirement is stated in the comment itself, so the citation is dropped rather than aimed at a section that does not say it. Comments that attributed a *sentence* to an old note — musicbay's "several thousand files" example, its "what got measured" note, its measured ~11%/~26% cover-art figures, the "original no root, whole shared tree" call — state the fact without attribution now. §9.8 does not contain those sentences and citing it for them would have been wrong. CLAUDE.md's "a reference to a document that no longer exists" row now says the concordance is for git history and out-of-tree material; the code cites sections directly. Verified: 2851 passed, 4 skipped. The 12 errors in the run are the Firefox leg of test_sticky_header.py's browser harness, which is broken at the browser level on this machine — headless Firefox (snap) dies with `[GFX1-]: RenderCompositorSWGL failed mapping default framebuffer`, renders nothing, and the probe exits `{"error": "no measurement"}` after its full 90s wait. Chrome runs the same 12 assertions in 3.2s and passes. Nothing here can affect it: every changed line in style.css is inside a comment. Also checked: ast.parse on every changed .py, `node --check` on every changed .js, the /* */ balance in style.css, and that no changed line exceeds the width its file already used. Co-Authored-By: Claude Opus 5 --- packages/meshbay-hub/src/meshbay_hub/api/users.py | 6 ++-- packages/meshbay-hub/src/meshbay_hub/config.py | 2 +- packages/meshbay-hub/src/meshbay_hub/db/models.py | 2 +- packages/meshbay-hub/src/meshbay_hub/mail.py | 4 +-- .../meshbay-hub/src/meshbay_hub/static/apps.js | 2 +- .../src/meshbay_hub/static/auth-page.js | 4 +-- .../src/meshbay_hub/static/file-utils.js | 4 +-- .../src/meshbay_hub/static/files-app.js | 2 +- .../src/meshbay_hub/static/group-name.js | 4 +-- .../src/meshbay_hub/static/group-page.js | 12 +++---- .../meshbay_hub/static/helloworld-app-settings.js | 7 ++-- .../src/meshbay_hub/static/helloworld-app.js | 4 +-- .../src/meshbay_hub/static/hub-client.js | 11 +++--- .../src/meshbay_hub/static/keyderive.js | 10 +++--- .../src/meshbay_hub/static/music-app-settings.js | 2 +- .../src/meshbay_hub/static/music-app.js | 39 +++++++++++----------- .../src/meshbay_hub/static/music-player.js | 18 +++++----- .../src/meshbay_hub/static/photos-app-settings.js | 2 +- .../src/meshbay_hub/static/photos-app.js | 4 +-- .../meshbay-hub/src/meshbay_hub/static/platform.js | 5 +-- .../src/meshbay_hub/static/profile-page.js | 4 +-- .../src/meshbay_hub/static/search-page.js | 8 ++--- .../src/meshbay_hub/static/settings-page.js | 2 +- .../src/meshbay_hub/static/source-merge.js | 2 +- .../meshbay-hub/src/meshbay_hub/static/style.css | 12 +++---- .../src/meshbay_hub/static/transport.js | 34 ++++++++++--------- .../src/meshbay_hub/static/video-app-settings.js | 2 +- .../src/meshbay_hub/static/video-app.js | 32 +++++++++--------- 28 files changed, 124 insertions(+), 116 deletions(-) (limited to 'packages/meshbay-hub/src') diff --git a/packages/meshbay-hub/src/meshbay_hub/api/users.py b/packages/meshbay-hub/src/meshbay_hub/api/users.py index 7b2fc8c..0394f53 100644 --- a/packages/meshbay-hub/src/meshbay_hub/api/users.py +++ b/packages/meshbay-hub/src/meshbay_hub/api/users.py @@ -107,7 +107,7 @@ class RegisterRequest(BaseModel): email: str password: str | None = None # deprecated — legacy native clients auth_key: str | None = None # PBKDF2-derived, new clients - # Client-generated account recovery key (docs/auth-confirm.md §4.4). Pure + # Client-generated account recovery key (docs/MESHBAY_DESIGN.md §3.6). Pure # pass-through: appended to the verification e-mail so the user's mailbox # backs it up, then dropped. Never written to any table, never logged. recovery_key: str | None = None @@ -901,7 +901,7 @@ async def verify_email_change( # ── Passphrase change (Flow A) ────────────────────────────────────────────── # -# docs/auth-confirm.md §3. The passphrase derives two independent values on the +# docs/MESHBAY_DESIGN.md §3.6. The passphrase derives two independent values on the # client: auth_key (verified here) and bundle_key (AES-GCM key for the per-node # identity bundles, which live on nodes and never on the hub). The client # re-wraps those bundles from the old bundle_key to the new one on every @@ -1023,7 +1023,7 @@ async def change_password( # ── Passphrase reset (Flow B) ────────────────────────────────────────────── # -# docs/auth-confirm.md §4.2. An e-mail code re-opens hub login for someone who +# docs/MESHBAY_DESIGN.md §3.6. An e-mail code re-opens hub login for someone who # has lost their passphrase. It recovers no group content — that needs the # recovery key, which the client applies on its own after the reset. # reset-request never reveals whether an account exists. diff --git a/packages/meshbay-hub/src/meshbay_hub/config.py b/packages/meshbay-hub/src/meshbay_hub/config.py index 876583e..f2212de 100644 --- a/packages/meshbay-hub/src/meshbay_hub/config.py +++ b/packages/meshbay-hub/src/meshbay_hub/config.py @@ -75,7 +75,7 @@ class CaptchaConfig: # Set this when that check is turned off in the reCAPTCHA console, which is # what the desktop client needs: its page is served from `app://meshbay`, # so the hostname Google sees is not the hub's and never can be. See - # docs/captcha.md §6. + # docs/MESHBAY_DESIGN.md §7.7. allowed_hosts: list[str] = field(default_factory=list) # A solve from a page Google cannot attribute to a domain reports an empty # hostname — `app://meshbay` does, measured live, and so does any other diff --git a/packages/meshbay-hub/src/meshbay_hub/db/models.py b/packages/meshbay-hub/src/meshbay_hub/db/models.py index f1fff41..62e4a11 100644 --- a/packages/meshbay-hub/src/meshbay_hub/db/models.py +++ b/packages/meshbay-hub/src/meshbay_hub/db/models.py @@ -201,7 +201,7 @@ class UserDevice(Base): directory *others* read from, where a substituted key was handed the GEK by an honest member. * **It is not a node identity key.** Those are generated per node, pinned - there, and never leave that relationship (`docs/per-node-identity-v1.md`). + there, and never leave that relationship (`docs/MESHBAY_DESIGN.md` §3.2). A device holds one of these *plus* a different key per node, so nothing here correlates a person across operators. diff --git a/packages/meshbay-hub/src/meshbay_hub/mail.py b/packages/meshbay-hub/src/meshbay_hub/mail.py index aa6b74e..126ed45 100644 --- a/packages/meshbay-hub/src/meshbay_hub/mail.py +++ b/packages/meshbay-hub/src/meshbay_hub/mail.py @@ -315,7 +315,7 @@ def send_verification_code(to: str, code: str, recovery_key: str | None = None) """ Registration verification e-mail. When `recovery_key` is given it is appended to the body so the recipient's mailbox becomes the backup for it - (docs/auth-confirm.md §4.4). + (docs/MESHBAY_DESIGN.md §3.6). `recovery_key` is a **pass-through**: it is generated on the client, never stored anywhere on the hub, and never logged — only whether one was present. @@ -374,7 +374,7 @@ def send_email_change_code(to: str, code: str) -> None: def send_password_reset_code(to: str, code: str) -> None: """ - Passphrase-reset code (docs/auth-confirm.md §4.2). This only re-opens hub + Passphrase-reset code (docs/MESHBAY_DESIGN.md §3.6). This only re-opens hub login; it recovers no group content — that needs the recovery key. """ msg = EmailMessage() diff --git a/packages/meshbay-hub/src/meshbay_hub/static/apps.js b/packages/meshbay-hub/src/meshbay_hub/static/apps.js index 461bd57..32f2314 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/apps.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/apps.js @@ -70,7 +70,7 @@ const APPS = [ Component: MusicApp, Settings: MusicSettings }, { key: 'photo', icon: 'image', labelKey: 'group.tab_photos', Component: PhotosApp, Settings: PhotoSettings }, - // The reference implementation (docs/refactor-groups.md §4.1). `dev` keeps + // The reference implementation (docs/MESHBAY_DESIGN.md §9.4). `dev` keeps // it out of an operator's way; everything else about it is an ordinary // entry, which is the point. { key: 'helloworld', icon: 'chat', labelKey: 'group.tab_helloworld', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/auth-page.js b/packages/meshbay-hub/src/meshbay_hub/static/auth-page.js index 04a00af..8958d44 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/auth-page.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/auth-page.js @@ -301,7 +301,7 @@ export function RegisterPage() { setLoading(true); try { if (window.MeshBayKeys) { - // The account recovery key (docs/auth-confirm.md §4.3/§4.4): generated + // The account recovery key (docs/MESHBAY_DESIGN.md §3.6): generated // here, shown once on the next screen. When the user leaves "e-mail it" // checked, the mnemonic goes in the register body so the hub appends it // to the verification e-mail (and stores it nowhere); otherwise it is @@ -511,7 +511,7 @@ export function RegisterPage() { } -// ── Passphrase reset — Flow B (docs/auth-confirm.md §4) ───────────────────── +// ── Passphrase reset — Flow B (docs/MESHBAY_DESIGN.md §3.6) ───────────────── // // An e-mail code restores hub login. A recovery key, if the user still has one, // restores the per-node identities in the same step: the fan-out reads each diff --git a/packages/meshbay-hub/src/meshbay_hub/static/file-utils.js b/packages/meshbay-hub/src/meshbay_hub/static/file-utils.js index b732734..decf916 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/file-utils.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/file-utils.js @@ -507,11 +507,11 @@ async function downloadEntry(transfers, transport, gek, entry) { * only alternative is to build the whole thing in memory — so that path is * offered but says what it costs first. * - * Lifted out of files-app.js (docs/photos.md §3) so photos-app.js's own + * Lifted out of files-app.js (docs/MESHBAY_DESIGN.md §9.9) so photos-app.js's own * "zip this album" button calls the same implementation rather than a * second one — nothing here is Files-specific once `entries`/`transport`/ * `gek`/`setError` are passed in, the same shared-context shape every app - * already receives (apps.md §2). + * already receives (docs/MESHBAY_DESIGN.md §9.2). */ async function downloadDirectory(transfers, transport, gek, entries, dir, { setError }) { if (!transport || !transport.connected) return; diff --git a/packages/meshbay-hub/src/meshbay_hub/static/files-app.js b/packages/meshbay-hub/src/meshbay_hub/static/files-app.js index 7629459..3f4212e 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/files-app.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/files-app.js @@ -314,7 +314,7 @@ function FilesPanel({ } }, [currentPath]); - // The implementation lives in file-utils.js (docs/photos.md §3) so + // The implementation lives in file-utils.js (docs/MESHBAY_DESIGN.md §9.9) so // photos-app.js's own "zip this album" button can call the same code // rather than a second one. const downloadDirectory = useCallback(async (dir) => { diff --git a/packages/meshbay-hub/src/meshbay_hub/static/group-name.js b/packages/meshbay-hub/src/meshbay_hub/static/group-name.js index f109da7..f0ace6a 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/group-name.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/group-name.js @@ -8,7 +8,7 @@ import { sourceLabel } from './source-merge.js'; * Group names are unique only per owner account (the hub enforces that), so the * `@handle` is what tells two groups called "photos" apart. `owner` is the * owner's username for a local group, or the source hub for a federated one - * (decision 5 in ~/next/groupnames.md); the caller decides which. + * (docs/MESHBAY_DESIGN.md §7.3); the caller decides which. * * `inline` renders "name@owner" on one line, for places that cannot take a * block — a badge, a `confirm()` string built elsewhere. @@ -29,7 +29,7 @@ export function GroupName({ name, owner, inline = false }) { * The Search view merges a file several groups share into one entry, so the * badge under a card cannot always name a group. One source keeps naming it * and keeps linking to it; more than one becomes a count, and *which* one was - * picked is deliberately not shown (docs/refactoring-search.md §5.5). + * picked is deliberately not shown (docs/MESHBAY_DESIGN.md §9.11). * * `entries` is the whole unit — every episode of a show, every track of an * album — not the entry the card was drawn from; `sourceLabel` explains why. diff --git a/packages/meshbay-hub/src/meshbay_hub/static/group-page.js b/packages/meshbay-hub/src/meshbay_hub/static/group-page.js index 205fce2..9a86f2a 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/group-page.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/group-page.js @@ -154,10 +154,10 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, // that). Null until the handshake ack arrives. const [scanSettings, setScanSettings] = useState(null); // TMDB on/off + whether a custom token is set, node-wide (not per-group) — - // docs/mediacenter.md §5.5. Null until the handshake ack arrives. + // docs/MESHBAY_DESIGN.md §9.7. Null until the handshake ack arrives. const [tmdbConfig, setTmdbConfig] = useState(null); // Which folders each app works over. One shape for all of them — a list, - // always, even where an app only wants one (docs/refactor-groups.md §1.6): + // always, even where an app only wants one (docs/MESHBAY_DESIGN.md §9.3): // Videos and Music were single values, which meant a library spread over two // drives could not be described at all. Empty means nothing configured yet, // which every app reads as "show nothing", never "the whole group index". @@ -171,7 +171,7 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, // Whether members' cross-group Search lists this group. Not an app setting: // it is about the group as a whole, and it hides nothing from this page. const [searchListed, setSearchListed] = useState(true); - // MusicBrainz on/off (per-group) — docs/musicbay.md §3.2. + // MusicBrainz on/off (per-group) — docs/MESHBAY_DESIGN.md §9.8. const [musicbrainzConfig, setMusicbrainzConfig] = useState(null); // `op` rides through to the shell — 'replace', 'next' or 'append' // (docs/playlists.md §9.2). It has to be named here: a wrapper that takes @@ -320,7 +320,7 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, setDeviceReady(false); gekRef.current = null; if (!session.bundleKey) session.bundleKey = await _loadBundleKey(); - // Persisted (docs/auth-confirm.md §4.3) so a group joined in a later + // Persisted (docs/MESHBAY_DESIGN.md §3.6) so a group joined in a later // session still leaves a recovery-wrapped identity copy on its node. if (!session.recoveryKey) session.recoveryKey = await _loadRecoveryKey(); if (!session.bundleKey && window.MeshBayKeys) { @@ -669,7 +669,7 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, // the only unambiguous answer once a group can have several writable roots. // Chat has no folder to browse, so it needs one picked for it, and this is // the same rule the node applies when a client names no root at all. It - // becomes an operator-chosen directory in phase 2 (refactor-groups.md §1.7). + // becomes an operator-chosen directory in phase 2 (docs/MESHBAY_DESIGN.md §9.6). // const writableRoots = useMemo( () => nodeRoots.filter((r) => r.writable && r.available !== false), @@ -735,7 +735,7 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, // rather than written out. Naming them here would mean adding an app // required editing this file, which is the one thing the plugin // architecture is supposed to have removed — and the reference app - // (docs/refactor-groups.md §4.1) is what made the difference visible. + // (docs/MESHBAY_DESIGN.md §9.4) is what made the difference visible. const perAppDirectories = useMemo(() => { const out = {}; for (const app of APPS) out[`${app.key}Directories`] = appDirs(app.key); diff --git a/packages/meshbay-hub/src/meshbay_hub/static/helloworld-app-settings.js b/packages/meshbay-hub/src/meshbay_hub/static/helloworld-app-settings.js index 9058199..be26fbc 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/helloworld-app-settings.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/helloworld-app-settings.js @@ -11,9 +11,10 @@ import { FolderPickerField } from './folder-tree.js'; * app's registry key by the page, and `ops.set_app_directories` stores the row * under that name without knowing what it is. * - * It takes the shared props and no others (`docs/apps.md` §3b), which is what - * `test_app_settings_plugin.py` checks of every pane — including this one, so - * the reference implementation is held to the contract it demonstrates. + * It takes the shared props and no others (`docs/MESHBAY_DESIGN.md` §9.2), + * which is what `test_app_settings_plugin.py` checks of every pane — including + * this one, so the reference implementation is held to the contract it + * demonstrates. */ function HelloWorldSettings({ roots, dirs, settings, saveDirectories }) { const { busy, msg, run } = useSaver(); diff --git a/packages/meshbay-hub/src/meshbay_hub/static/helloworld-app.js b/packages/meshbay-hub/src/meshbay_hub/static/helloworld-app.js index 01b4ed4..98ca8fa 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/helloworld-app.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/helloworld-app.js @@ -7,7 +7,7 @@ import { formatSize } from './file-utils.js'; * The smallest application this platform can host, and the proof that adding * one costs nothing outside its own two files. * - * Everything else in `docs/refactor-groups.md` §3 is asserted by tests that + * Everything else in `docs/MESHBAY_DESIGN.md` §9.4 is asserted by tests that * read source. This is the other kind of evidence: an app nobody wrote a line * of plumbing for, that stores directories, appears as a tab, and lists files — * because the registry entry beside it is genuinely all there is. @@ -18,7 +18,7 @@ import { formatSize } from './file-utils.js'; * put a toy app in everybody's group; deleting the app would leave the claim * resting entirely on tests that read text. * - * It takes the standard props — see `docs/apps.md` §2 — and reads + * It takes the standard props — see `docs/MESHBAY_DESIGN.md` §9.2 — and reads * `helloworldDirectories`, which nothing on the node knows about by name: * `ops.set_app_directories` keys the row by whatever the app is called. */ diff --git a/packages/meshbay-hub/src/meshbay_hub/static/hub-client.js b/packages/meshbay-hub/src/meshbay_hub/static/hub-client.js index 81185a5..f467720 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/hub-client.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/hub-client.js @@ -136,7 +136,7 @@ async function clearAllCachedIndexes() { // so importing modules can update either field without this module handing // out a rebindable export. // -// `recoveryKey` (docs/auth-confirm.md §4.3) is the AES key that wraps the +// `recoveryKey` (docs/MESHBAY_DESIGN.md §3.6) is the AES key that wraps the // *recovery* copy of an identity bundle. In-memory only, and set only when the // user has just generated or entered the recovery secret (registration, or the // Flow B screen) — it cannot be re-derived from the passphrase. @@ -169,10 +169,11 @@ async function _loadKey(slot) { return val || null; } catch { return null; } } -// 'bk' = passphrase-derived bundle key; 'rk' = recovery key (docs/auth-confirm.md -// §4.3). Persisting 'rk' is what lets a group joined in a *later* session still -// get a recovery-wrapped identity copy, instead of only groups joined in the -// unbroken session that generated it. Cleared with everything else on sign-out. +// 'bk' = passphrase-derived bundle key; 'rk' = recovery key +// (docs/MESHBAY_DESIGN.md §3.6). Persisting 'rk' is what lets a group joined +// in a *later* session still get a recovery-wrapped identity copy, instead of +// only groups joined in the unbroken session that generated it. Cleared with +// everything else on sign-out. const _storeBundleKey = (key) => _storeKey('bk', key); const _loadBundleKey = () => _loadKey('bk'); const _storeRecoveryKey = (key) => _storeKey('rk', key); diff --git a/packages/meshbay-hub/src/meshbay_hub/static/keyderive.js b/packages/meshbay-hub/src/meshbay_hub/static/keyderive.js index edfa109..879f56f 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/keyderive.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/keyderive.js @@ -191,7 +191,7 @@ async function deriveBundleKeys(password, username) { // ── Account recovery key ───────────────────────────────────────────────────── // -// docs/auth-confirm.md §4.3. A full-entropy secret the user keeps outside the +// docs/MESHBAY_DESIGN.md §3.6. A full-entropy secret the user keeps outside the // passphrase — in their password manager, or (step 3) e-mailed to them. It // wraps a *second* copy of every per-node identity bundle, so a forgotten // passphrase does not strand the account's group identities. @@ -323,7 +323,7 @@ async function registerUser(username, email, password, recoveryMnemonic, captcha const payload = { username, email, auth_key: authKey }; // The recovery mnemonic, when the user opted to have it e-mailed: the hub // appends it to the verification e-mail and stores it nowhere - // (docs/auth-confirm.md §4.4). Omitted when they chose to save it themselves. + // (docs/MESHBAY_DESIGN.md §3.6). Omitted when they chose to save it themselves. if (recoveryMnemonic) payload.recovery_key = recoveryMnemonic; // reCAPTCHA response, when the hub has a captcha configured. The widget lives // in RegisterPage (auth-page.js); this function just forwards its token. A @@ -347,7 +347,7 @@ async function registerUser(username, email, password, recoveryMnemonic, captcha * bundle goes to that node and nowhere else, and is what any other browser * fetches to become the same person there. When `recoveryKey` is supplied a * second copy wrapped under it rides along, so a forgotten passphrase does not - * strand this identity (docs/auth-confirm.md §4.3). + * strand this identity (docs/MESHBAY_DESIGN.md §3.6). */ async function generateNodeIdentity(bundleKey, recoveryKey) { const { skEdRaw, pkEdRaw, skXRaw, pkXRaw } = await generateKeypairs(); @@ -465,7 +465,7 @@ async function _bundleKeyPairFields(password, username) { window.MeshBayKeys = { registerUser, loginAndRecover, generateNodeIdentity, generateKeypairs, signBytes, deriveAuthKey, decryptBundleWithKey, encryptBundleWithKey, bundleVersion, - // Exposed for the passphrase change (docs/auth-confirm.md §3): re-wrapping a + // Exposed for the passphrase change (docs/MESHBAY_DESIGN.md §3.6): re-wrapping a // node's identity bundle needs the old key (a {v2,v1} pair, since an old // bundle may be v1) to read it and the new v2 key to write it back. deriveEncryptionKey, deriveEncryptionKeyV1, @@ -473,6 +473,6 @@ window.MeshBayKeys = { // `session.bundleKey` uses this, so `v2hkdf` is never the field one sign-in // path forgot (docs/playlists.md §3.4). deriveBundleKeys, bundleKeyPairFields: _bundleKeyPairFields, - // Account recovery key (docs/auth-confirm.md §4.3). + // Account recovery key (docs/MESHBAY_DESIGN.md §3.6). generateRecoveryKey, deriveRecoveryKey, }; diff --git a/packages/meshbay-hub/src/meshbay_hub/static/music-app-settings.js b/packages/meshbay-hub/src/meshbay_hub/static/music-app-settings.js index 8ce2ab6..a9a3c95 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/music-app-settings.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/music-app-settings.js @@ -8,7 +8,7 @@ import { FolderPickerField } from './folder-tree.js'; * * Same shape as Videos, minus a credential: MusicBrainz's read endpoints need * no API key, only a descriptive User-Agent, and that is one operator identity - * held node-wide rather than a per-group setting (docs/musicbay.md §3.2). + * held node-wide rather than a per-group setting (docs/MESHBAY_DESIGN.md §9.8). * * Several folders, for the same reason Videos has several: a music library * that lives on two drives had no way to say so. diff --git a/packages/meshbay-hub/src/meshbay_hub/static/music-app.js b/packages/meshbay-hub/src/meshbay_hub/static/music-app.js index baa3520..e0308cb 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/music-app.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/music-app.js @@ -15,13 +15,13 @@ import * as P from './playlists.js'; // // An album-grid (MusicBrainz-enriched, when a track has no usable embedded // cover) or flat (tag/filename-only) browser for a group's audio files, per -// docs/musicbay.md. Grouping is by `artist`/`album` -- already resolved at -// index time from embedded tags, falling back to filename/folder parsing -// (indexer/enrich_audio.py) -- never guessed here. +// docs/MESHBAY_DESIGN.md §9.8. Grouping is by `artist`/`album` -- already +// resolved at index time from embedded tags, falling back to filename/folder +// parsing (indexer/enrich_audio.py) -- never guessed here. // // Unlike Videos, MusicBrainz is looked up only when a track has no embedded -// cover art at all (musicbay.md section 2.1's tiered trust: tags first, -// filename parsing second, MusicBrainz last) -- most of a real, well-ripped +// cover art at all (docs/MESHBAY_DESIGN.md §9.8's order of trust: tags +// first, filename parsing second, MusicBrainz last) -- most of a real, well-ripped // library already carries good artist/album text and often its own cover, so // this avoids a network round trip most tiles never need. Playback never // touches this file: clicking a track calls the `onPlayQueue` prop the shell @@ -52,7 +52,7 @@ function foldKey(s) { } // Same shape as video-app.js's underVideoRoot: an unset root means "show -// nothing" (docs/musicbay.md's amended §2.1 — the node itself runs no +// nothing" (docs/MESHBAY_DESIGN.md §9.8 — the node itself runs no // tag/cover enrichment for this group before a root is chosen either, // daemon.py's _enrich_new_audio_entries), not "the whole shared tree" — // falling back to that would just show files nothing has enriched. @@ -212,17 +212,16 @@ function useMusicMeta(transportRef, fileId, active) { } // A drawn CD standing in for a cover nothing supplied one for -- most tiles -// in a real, older/well-ripped library land here (musicbay.md's own -// measurement: ~11% embedded art, ~26% once sibling image files are counted -// too), so this is the *default* look of the grid, not a rare fallback, and -// needed to read as a deliberate piece of art rather than a broken image. -// A flat single-color icon (the first version of this) looked exactly like -// "missing", not "no cover" -- an actual disc, with the iridescent sheen a -// real CD's data side has, reads as intentional at a glance. Genuinely -// unique gradient ids: a `` id is a plain DOM id, and a grid -// full of these renders many instances at once -- reusing one literal id -// would leave every disc after the first pointing at whichever def the -// browser resolves first. +// in a real, older/well-ripped library land here (measured: ~11% embedded +// art, ~26% once sibling image files are counted too), so this is the +// *default* look of the grid, not a rare fallback, and needed to read as a +// deliberate piece of art rather than a broken image. A flat single-color icon +// (the first version of this) looked exactly like "missing", not "no cover" -- +// an actual disc, with the iridescent sheen a real CD's data side has, reads +// as intentional at a glance. Genuinely unique gradient ids: a +// `` id is a plain DOM id, and a grid full of these renders +// many instances at once -- reusing one literal id would leave every disc +// after the first pointing at whichever def the browser resolves first. let _discIdSeq = 0; function DiscPlaceholder({ cls }) { @@ -517,8 +516,8 @@ function FlatArtistFolder({ artist, onPlayQueue, onMenu }) { ${open && html`
${/* A real artist folder with no album layer at all is common here - -- a pile of loose singles, not one release (musicbay.md - section 2.1's "flat per-artist folder" case). Nesting them + -- a pile of loose singles, not one release (a flat + per-artist folder, docs/MESHBAY_DESIGN.md §9.8). Nesting them one more level behind their own always-empty "Unknown album" row was exactly the friction reported live: an extra, pointless expand before reaching a track that's playable @@ -748,7 +747,7 @@ function MusicApp({ } // foldKey rides along for the Search page's merge unit keys -// (docs/refactoring-search.md §5.2). An album's *display* strings are the +// (docs/MESHBAY_DESIGN.md §9.11). An album's *display* strings are the // first-seen spelling, and which group is seen first is the order its index // happened to arrive in — so keying a unit on them would let the chosen source // change between page loads. The folded key is the one grouping actually used, diff --git a/packages/meshbay-hub/src/meshbay_hub/static/music-player.js b/packages/meshbay-hub/src/meshbay_hub/static/music-player.js index c3a2868..7c81f4e 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/music-player.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/music-player.js @@ -8,7 +8,7 @@ import { useStickyBand } from './sticky.js'; import { CHUNK_SIZE, pipelinedDownload } from './file-utils.js'; /** - * The Music app's persistent player bar (docs/musicbay.md §2.3, §7.2). + * The Music app's persistent player bar (docs/MESHBAY_DESIGN.md §9.8). * * Owned and rendered by app.js — the router's parent — so playback survives * navigating between groups, search, and other pages. Both group-page.js @@ -23,11 +23,12 @@ import { CHUNK_SIZE, pipelinedDownload } from './file-utils.js'; * track is a few megabytes, so it is downloaded and decrypted once through * the same chunk pipeline Files already uses (file-utils.js's * pipelinedDownload), then played from a blob URL — the deliberate - * simplification recorded in musicbay.md §2.2. WMA and Musepack are the one - * exception (`NEEDS_TRANSCODE_RE` below): neither decodes in a browser's - *