From 32a86417d0edc3bf5c4cf859243c9e34b29bf1ef Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Thu, 10 Sep 2026 19:26:10 +0200 Subject: fix(node): the handshake ack dropped one app's directories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ack was assembled from its own tuple of application names, a copy of the daemon's `APP_DIR_KEYS`, and the two had drifted: the copy was missing `helloworld`. So the reference application — the one that exists to prove a new application needs no special-casing — was the single application whose configured folders never reached a client, which made the plugin claim false exactly where it is demonstrated. Fixed by removing the copy rather than syncing it. The ack now emits whatever `_directories` the group context carries, and `_app_directories_ctx` is the only thing that puts one there, so the two cannot disagree again. The transport names an application in one place, `ALLOWED_APPS`, which is enforcement rather than a directory list. The client had the same fault one layer up: `group-page.js` read three names by hand from the ack while the live-update path beside it was already generic. It derives the map from the ack's own keys now, so the fix reaches the settings pane instead of stopping at the wire. A first attempt moved the list to `roster.py`, where directory *storage* lives, and `test_helloworld_proves_the_plugin_claim.py` refused it: the roster, the ops, the config and the root set must name no application at all. That test is the architecture's own guard and it was right — the list belongs on the daemon, which is what wires a group's context, and everything downstream is derived from it. Two new tests, both verified to fail against the previous shape: the ack carries an application the node names nowhere else, and the ack keeps no list of its own. `test_the_lists_are_read_under_one_name_each` now asserts the shell names no application rather than that it names exactly three. Two stale comments went with it — the ack's, which described scalars removed in 07ff8b4, and the client's, which said those scalars still rode the wire for MNP 1.0 peers that can no longer connect. Full suite: 2258 passed, 4 skipped. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01YVoHVCcfBqud6ZjG4db3y7 --- docs/MESHBAY_DESIGN.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md index 73f6b42..67f0bff 100644 --- a/docs/MESHBAY_DESIGN.md +++ b/docs/MESHBAY_DESIGN.md @@ -1952,7 +1952,9 @@ There is no folder-browsing protocol and this does not add one. under. **One identifier per application, everywhere.** 3. **`-app-settings.js`** if it has anything to configure. Do not import the settings page — that is the cycle in §9.1. -4. **Add the key to the node's allow-list.** +4. **Add the key to the node's allow-list**, and — if the application keeps + directories — to `NodeDaemon.APP_DIR_KEYS`, the one list a group's context is + built from. Nothing else on the node may name an application. 5. **i18n:** at minimum a tab label key **in all ten catalogues**. A settings key added to the client must be added ten times; write the table and generate the insert. @@ -1971,6 +1973,26 @@ There is no folder-browsing protocol and this does not add one. No protocol change, no hub change, no daemon change. Steps 4 and 7 are the only node-side and test-side touches, and both are allow-lists. +> **A list of application names is only ever kept in one place, and everything +> downstream is derived from it.** There were three. The daemon built a group's +> context from one; the handshake ack was assembled from a copy that had already +> lost an entry — the reference application's, so the one application that exists +> to prove a new one needs no special-casing was the single one whose directories +> never reached a client; and the client shell named three applications by hand +> while the live-update path beside it was already generic. +> +> The fix that holds is **removing the copies, not syncing them**: the ack emits +> whatever `_directories` the context carries, and the shell reads the ack's +> own keys. Neither can drift, because neither has anything of its own to drift +> from. +> +> **Where the one list lives matters too.** It is on the daemon, which is what +> wires a group's context; the roster, the operator ops, the config and the root +> set must name no application at all, and a test holds them to it. That is the +> property the reference application exists to demonstrate, and it is the reason +> a first attempt at this fix — moving the list to the roster, where the +> directory *storage* lives — was wrong and was caught. + **A reference application exists in the tree behind a development flag.** Every other test of this architecture reads source for the *absence* of application names, which proves nobody wrote a special case — not that a new application works. -- cgit v1.2.3